Currently Available: Need a skilled Software Developer for your next project?
Categories
APIs

How to Fix CORS Preflight Errors for a JSON API

When browser code sends JSON to an API on a different origin, the browser may send an OPTIONS request before the actual request. That check is called a CORS preflight. CORS, or Cross-Origin Resource Sharing, is the browser’s permission check for letting a page read a response from a different origin. An origin combines a page’s protocol and domain with its port. A difference in any of those makes the request cross-origin. CORS guidance

The API must answer the preflight with headers that allow the page’s origin. Those headers must also permit the HTTP method and request headers. If it does not, the browser blocks the actual request or prevents JavaScript from reading its response. To fix the error, inspect the browser’s OPTIONS request, then configure the API or its proxy to answer with the permissions that request needs.

Inspect the preflight in the browser

Open the browser’s developer tools, select the Network panel, and reproduce the failing API call. Find the OPTIONS request to the API endpoint. The browser sends this request first to check whether the API permits the actual request. For example, a JSON POST with an Authorization header might produce a preflight like this:

OPTIONS /api/items
Origin: https://frontend.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization, content-type

The API’s response needs to allow the page’s origin, the requested method, and the requested headers:

Access-Control-Allow-Origin: https://frontend.example
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: authorization, content-type

Treat https://frontend.example as a placeholder and use the exact origin shown in your browser’s request. The API should list the method the browser plans to send, such as POST; the preflight itself uses OPTIONS.

If the browser reports that Access-Control-Allow-Origin is missing, check whether the OPTIONS response includes it. If the error names a method or header, compare that name with Access-Control-Allow-Methods or Access-Control-Allow-Headers. The browser’s Access-Control-Request-Headers value tells you which request headers the API needs to allow.

A response in JSON does not by itself trigger a preflight. The browser sends one if the request uses a method outside the set allowed for a simple request. A custom header such as Authorization also triggers a preflight, as does a content type such as application/json. JSON clients commonly meet more than one of these triggers. The preflight request flow shows why the API must answer OPTIONS before the browser sends the actual request.

Make the API answer OPTIONS

Configure the API to handle preflight requests on the same path as the real endpoint. The response must include the CORS headers the browser requested, and the API must also include the appropriate CORS headers on the actual response so JavaScript can read it.

In Express, the cors middleware is code that handles CORS before route handlers and can answer preflight requests. Registering it at the application level lets it handle preflight requests across routes:

const cors = require('cors');

app.use(cors({
  origin: 'https://frontend.example',
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));

Replace the example origin, methods, and headers with the values your API needs. Register CORS middleware before authentication or route middleware that might reject OPTIONS; otherwise, that earlier middleware can stop the preflight before the CORS handler responds.

For another framework, apply the same checks: the route or middleware must accept OPTIONS, and the response must allow the requested origin, method, and headers. If a reverse proxy, which forwards requests to the API, such as NGINX, adds CORS headers, check that both the proxy and the API are not adding Access-Control-Allow-Origin. A response containing duplicate values such as *,* fails the browser’s check. Check the NGINX configuration for duplicate Access-Control-Allow-Origin headers.

Match the origin and credentials

For requests without credentials, an API can allow a specific origin or use * when the endpoint is intended for any origin. For requests that include cookies or other browser credentials, the API must return the exact allowed origin and Access-Control-Allow-Credentials: true. Browsers reject Access-Control-Allow-Origin: * when the request’s credentials mode is include. Credentialed CORS guidance

When an API picks an allowed origin from a list of permitted origins, it should also send Vary: Origin. This tells shared caches to keep separate responses for different origins instead of reusing one origin’s CORS response for another.

Check both the preflight response and the actual API response when credentials are involved. Also confirm the browser client sends credentials as intended. For cookies sent with cross-site API requests, the cookie must use SameSite=None and Secure. Different origins can still belong to the same site. Browser cookie rules can still affect whether the cookie is sent.

Remove headers the browser should not send

Access-Control-Allow-Origin and the other Access-Control-Allow-* headers belong in the server’s response. Do not add them to a fetch or Axios request. If the browser says Access-Control-Allow-Origin is not allowed by Access-Control-Allow-Headers, remove it from the client’s request headers and configure it in the API response instead.

The API should allow the request headers the browser actually sends. A JSON client commonly sends Content-Type: application/json; Axios also sets that content type on requests with a body. If an interceptor, client code that modifies requests, adds Authorization or another custom header, include that header in the API’s Access-Control-Allow-Headers response.

Changing the request to mode: 'no-cors' does not fix a JSON API call that needs to read its response. That mode produces an opaque response, which JavaScript cannot read or inspect. During local development, a proxy can route browser requests through the frontend’s origin, but production cross-origin requests still need the API or its proxy to provide the right CORS headers.

What I'm building

Delegate tasks. Get software.

Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.

Take a look at vroni.com

Email updates

Usually a new article and a few links I found interesting.

No spam. Unsubscribe with one click.

Leave a Reply

Your email address will not be published. Required fields are marked *