Why am I getting CORS errors on preflight requests and how to fix them?

Asked 22 days ago Updated 20 hours ago 119 views

0

I am building a React frontend that connects to a Node.js Express backend hosted on a separate domain. Simple GET requests work fine, but POST and PUT requests fail with a CORS error during the preflight phase. How should OPTIONS requests be handled correctly on the server?

Express CORS Configuration

Preflight requests use the HTTP OPTIONS method. Here is how to configure Express to return proper CORS headers.

const express = require('express');
const app = express();

// Handle CORS preflight explicitly
app.use((req, res, next) => {
    // Allow requests from frontend domain
    res.header('Access-Control-Allow-Origin', 'https://app.example.com');
    // Specify allowed headers
    res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
    // Specify allowed HTTP methods
    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    
    // Intercept preflight OPTIONS request
    if (req.method === 'OPTIONS') {
        return res.sendStatus(200);
    }
    next();
});

1 Answer


0

A CORS preflight is the browser’s way of checking whether a server will allow a cross-origin request before sending the actual request. It sends an OPTIONS request first, usually when the request uses a method such as PUT or includes non-simple headers like Authorization.

If that check fails, the browser reports a CORS error and does not send the actual request. The useful clue is usually in the preflight response or the browser’s Network panel, rather than in the request your application intended to make.

Check the OPTIONS response

The server, or a proxy in front of it, must answer the preflight with a successful status and the appropriate CORS headers. Check that the response includes:

  • Access-Control-Allow-Origin with the page’s exact origin, such as https://app.example.com.
  • Access-Control-Allow-Methods listing the requested method.
  • Access-Control-Allow-Headers listing the headers named in the browser’s Access-Control-Request-Headers.

A common cause is that the application handles regular API routes but not OPTIONS. Another is middleware that rejects the preflight because it expects a login token. Preflight requests generally do not carry the credentials from the actual request, so allow the OPTIONS check through before authentication. Also check that a proxy, CDN, or redirect is not intercepting the request.

Example with Express

For a single allowed frontend origin, CORS handling can look like this. Register it before your routes and authentication middleware:

const allowedOrigin = "https://app.example.com";

// Add CORS headers to both preflight and actual API responses.
app.use((req, res, next) => {
  res.setHeader("Access-Control-Allow-Origin", allowedOrigin);
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");

  // End the browser's permission check without running the API route.
  if (req.method === "OPTIONS") {
    return res.sendStatus(204);
  }

  next();
});

If you allow several origins dynamically, validate the incoming Origin against an allowlist and return the matching origin; add Vary: Origin so caches do not mix responses. For credentialed requests, such as requests using cookies, also send Access-Control-Allow-Credentials: true. In that case, the allowed origin cannot be *.

Compare the preflight’s requested method and headers with the server’s allow lists. The fix belongs on the server or proxy that answers the OPTIONS request—not in frontend code that tries to bypass the browser’s CORS checks.

Write Your Answer