TroubleshootingIntermediate

How to Fix CORS Errors When Calling an AI API From the Browser

Stop CORS failures and protect your key by routing AI API calls through a small backend proxy.

9 minIntermediate

When you call an AI provider directly from front-end JavaScript, the browser often blocks it with a CORS error, and even if it worked it would leak your secret key to every visitor. The correct fix solves both problems at once: call your own backend, and have the backend call the provider with the key kept server side.

  • A front-end app making fetch calls
  • A small server you control (Node, a serverless function, or similar)
  • Your provider API key stored as a server secret

Step 1: Read the console error

The browser console spells out the block. It refuses the response because the provider does not send an Access-Control-Allow-Origin header for your site. You cannot add that header to someone else's server, which is why a proxy is the answer.

Browser console
Access to fetch at 'https://api.openai.com/v1/chat/completions'
from origin 'http://localhost:3000' has been blocked by
CORS policy: No 'Access-Control-Allow-Origin' header is
present on the requested resource.
The provider blocks direct browser calls by design.
Never put your key in front-end code
Anything in the browser is visible to users. A key shipped to the client will be stolen. The proxy below keeps it on the server.

Step 2: Add a backend route that holds the key

Create one server endpoint that accepts the prompt from your front end, calls the provider with the secret key, and returns the result. This endpoint has the same origin as your app, so the browser is happy.

server.js
import express from "express";
import OpenAI from "openai";

const app = express();
app.use(express.json());
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

app.post("/api/chat", async (req, res) => {
  const out = await client.chat.completions.create({
    model: "gpt-4o-mini",
    messages: req.body.messages,
  });
  res.json(out);
});

app.listen(3001);

Step 3: Point the front end at your route

Change the front-end fetch to hit your own /api/chat path instead of the provider URL. No key, no provider domain, no CORS block.

app.js
const res = await fetch("/api/chat", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ messages }),
});
const data = await res.json();

Step 4: Verify the request stays same origin

Open the network tab and confirm the call goes to your domain and returns a 200. The provider call now happens server to server, where CORS does not apply.

zsh - test
$curl -X POST localhost:3001/api/chat -H "content-type: application/json" -d '{"messages":[{"role":"user","content":"hi"}]}'
{ "choices": [ { "message": { "content": "Hello! How can I help?" } } ] }
$
Add rate limiting to your proxy
Because your proxy is now public, add basic auth or a per-IP rate limit so strangers cannot run up your bill through it.

Result

The CORS error disappears, the key never leaves the server, and the front end talks only to your own API. This is the standard pattern every production AI app uses.

Watch related tutorials

Tags
#cors#browser#proxy#api keys