Uncover why external APIs trigger CORS issues in Lovable apps, learn to fix them in full-stack projects, and adopt best practices.

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
External APIs trigger CORS issues in Lovable because browsers enforce the Same-Origin Policy for requests made from client-side code. If your app (running from Lovable's preview/published origin) makes a direct browser request to an external domain that does not include the appropriate Access-Control-Allow-Origin response for your origin — or does not respond correctly to preflight OPTIONS requests — the browser will block the response. In practice this happens most often when code that should run server-side runs in the browser, when requests include custom headers or non-simple methods that cause preflight, or when the external API simply doesn't expose CORS headers for Lovable's preview/published origin.
Browser vs server enforcement: CORS is enforced by browsers only. Server-to-server calls (from Lovable server functions / API routes) do not hit CORS in the browser; direct client fetches do.
Preflight and “simple” requests: Requests that use custom headers, non-standard Content-Type, or HTTP methods other than GET/POST may trigger an OPTIONS preflight. If the external API doesn't reply to OPTIONS with the correct Access-Control-Allow-\* headers, the browser blocks the call.
Common developer mistakes in Lovable: Assuming local dev behavior matches Lovable preview (local dev might use a proxy or different origin), or assuming third-party APIs will allow arbitrary origins. Also, code placed in client bundles (components) instead of server functions will unexpectedly surface CORS errors in Preview/Publish.
// Please scan the project and create a diagnostics file that identifies every external API call
// and whether it's executed client-side (will trigger browser CORS) or server-side (won't).
// Create diagnostics/CORS-EXTERNAL-API-REPORT.md with findings.
Task:
- Search these paths for external requests and list each match: src/, app/, pages/, components/, lib/, services/, routes/, api/, functions/
- Match patterns: fetch( , axios., new XMLHttpRequest( , window.fetch, globalThis.fetch, fetchJson, ky( , io( // for websockets
- For each match include:
// file path and line snippet (3 lines)
// detected external domain (host)
// whether the code runs client-side (component, useEffect, event handler) or server-side (pages/api, server function)
// whether the request uses custom headers, credentials, or non-simple methods (PUT, DELETE, PATCH)
// a short note: "will trigger browser CORS" or "server-side - no browser CORS"
- Add a summary section listing unique external domains and why they will fail in Lovable preview if they lack CORS headers (explain preflight/credential reasons per domain).
- If you cannot determine runtime origin (preview vs publish), explain that Lovable preview origin differs from localhost and must be considered.
// Optional follow-up prompt to run in Lovable Chat after the scan completes:
// Create diagnostics/CORS-EXTERNAL-API-REPORT.md from the scan results and open it in the editor.
// Make the report very explicit so a non-technical teammate can see which files to inspect.
This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.
The simple fix is to stop relying on the browser to call cross-origin APIs directly and instead give the browser responses that include the proper CORS headers. In Lovable you do that by adding CORS handling on your server-side routes (serverless API routes), or by creating a server-side proxy endpoint that calls the external API using a secret (added in Lovable’s Secrets UI) and returns results with Access-Control-Allow-* headers. Below are ready-to-paste Lovable chat prompts that will implement both patterns and configure secrets.
Paste this into Lovable chat. It will create a reusable CORS helper at api/\_cors.ts and update any existing server route example api/hello.ts to use it. If your routes live in a different folder, tell Lovable to apply the same changes there.
Please create a reusable CORS helper and wire it into our server API routes.
1) Create a new file at api/_cors.ts with this code:
// add CORS headers and handle OPTIONS preflight
export function withCors(handler) {
return async function (req, res) {
// allow the origin from the request (or set a fixed origin)
const origin = req.headers.origin || '*';
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
res.setHeader('Access-Control-Allow-Credentials', 'true');
// handle preflight
if (req.method === 'OPTIONS') {
res.statusCode = 204;
res.end();
return;
}
// call the original handler
return handler(req, res);
};
}
2) Update api/hello.ts (if present) to use the helper. Replace the default export handler with the wrapped version:
import { withCors } from './_cors';
async function handler(req, res) {
// // original handler code goes here
res.json({ hello: 'world' });
}
export default withCors(handler);
3) Scan api/ for other route files (*.js, *.ts) and wrap their exported handlers with withCors in the same way.
Paste this into Lovable chat to add api/proxy.ts. The proxy uses a secret named EXTERNAL_API_KEY — add that in Secrets UI (instructions follow).
Create a server-side proxy endpoint at api/proxy.ts that forwards requests to an external API and returns responses with CORS headers. Use process.env.EXTERNAL_API_KEY for the API key.
Create file api/proxy.ts with this code:
// proxy endpoint that handles preflight and forwards requests
import fetch from 'node-fetch'; // // Lovable runtime supports fetch; if not, use global fetch
import { withCors } from './_cors';
async function handler(req, res) {
const targetBase = 'https://api.example.com'; // // replace with the real external API base URL
const path = req.url.replace(/^\/api\/proxy/, '');
const targetUrl = targetBase + path;
// forward method, headers, and body
const headers = {
'Content-Type': req.headers['content-type'] || 'application/json',
// // send API key from Secrets
Authorization: `Bearer ${process.env.EXTERNAL_API_KEY || ''}`,
};
const fetchOptions = {
method: req.method,
headers,
// // forward body for non-GET
body: ['GET','HEAD'].includes(req.method) ? undefined : await streamToBuffer(req),
};
const upstream = await fetch(targetUrl, fetchOptions);
const body = await upstream.buffer();
// mirror upstream status and headers
res.statusCode = upstream.status;
res.setHeader('Content-Type', upstream.headers.get('content-type') || 'application/json');
res.end(body);
}
// helper to read request body
async function streamToBuffer(req) {
const chunks = [];
for await (const chunk of req) chunks.push(chunk);
return Buffer.concat(chunks);
}
export default withCors(handler);
Use Lovable’s Secrets UI to set the secret referenced above. Paste this into Lovable chat as an instruction if you want Lovable to remind you or document the steps.
Add a secret in Lovable Secrets UI:
- Name: EXTERNAL_API_KEY
- Value: <paste the real API key here>
- Description: API key for external service used by api/proxy.ts
Please confirm the secret is saved and available in the Preview/Publish environment.
Prioritize same-origin requests and server-side requests inside Lovable (server functions or your backend) whenever possible, store secrets in Lovable Secrets, test via Preview, and keep CORS configuration on the API-side (or the service dashboard) rather than trying client-side workarounds. These practices minimize brittle browser CORS blockers, avoid leaking credentials, and map cleanly to Lovable’s no-terminal workflow (use Chat edits, Secrets UI, Preview, Publish and GitHub sync when needed).
Paste these into Lovable’s chat to make concrete changes. Each prompt is explicit about file paths and steps. Use Preview after each change.
// Prompt: Create a server-side API route that forwards requests to the third-party API and reads secrets from Lovable Secrets.
// Create file at src/api/proxy.ts
// This route should accept GET and POST, fetch the external API using a secret named THIRD_PARTY_KEY, and return the external response.
// Include appropriate response headers for JSON. Keep secrets out of client code.
Please create a new server API route at src/api/proxy.ts with code that:
- Reads the secret THIRD_PARTY_KEY via the Lovable Secrets environment (do not hardcode).
- Accepts requests and forwards them to the external API (preserve path and method).
- Returns the external API response body and status to the client with application/json.
- Does not expose secrets to the client or logs.
- Add comments explaining where to change the external API base URL.
// Prompt: Update the client to call the same-origin proxy instead of the external host.
// Modify file src/lib/apiClient.ts (or src/utils/api.ts if that is your file).
// Replace any direct fetch('https://api.external/...') calls with fetch('/api/proxy/...') or the equivalent relative path.
// Add a short comment explaining why we use same-origin proxy.
Please update src/lib/apiClient.ts to use relative paths that call /api/proxy/*, and add a comment: // Use same-origin proxy to avoid browser CORS and keep keys on server
// Prompt: Add a note and secret in Lovable Secrets UI.
// Use Lovable Secrets to create a secret named THIRD_PARTY_KEY (value: your API key).
// Do NOT paste the key in code or chat. After you add the secret, update deployment env mapping if prompted.
Please open the Lovable Secrets UI and add a secret named THIRD_PARTY_KEY. Then confirm the server route src/api/proxy.ts reads this secret name for calls.
// Prompt: Add documentation and tests to validate behavior in Preview.
// Create docs/README-cors.md describing the pattern and flow, and a small client test page at src/pages/test-cors.tsx that calls /api/proxy and shows status.
// This helps reviewers test in Preview before Publish.
Please add docs/README-cors.md (explain the proxy + secrets pattern) and create src/pages/test-cors.tsx that calls /api/proxy and displays response status for manual testing in Preview.
Follow these prompts, use Preview to verify runtime behavior, and only publish once the behavior is stable. These patterns keep your Lovable app safe, testable inside Preview, and compatible with Lovable’s no-terminal workflow.
From startups to enterprises and everything in between, see for yourself our incredible impact.
Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.