The browser’s Same-Origin Policy, in conjunction with CORS headers, is preventing your REST API from being accessed by web applications served from a different domain, protocol, or port.

This happens because, by default, browsers enforce a security boundary. When a web page (origin A) tries to make an AJAX request to a different origin (origin B), the browser first checks if origin B has explicitly allowed this cross-origin request. If origin B hasn’t signaled its permission via specific HTTP headers (CORS headers), the browser blocks the request before it even reaches your API, and you’ll see a CORS error in the browser console.

Common Causes and Fixes:

  1. Missing Access-Control-Allow-Origin Header:

    • Diagnosis: Use your browser’s developer tools (Network tab). When the request fails, inspect the response headers from your API. If Access-Control-Allow-Origin is absent, this is your problem.
    • Fix: Configure your API server to include this header. For development, you might allow all origins: Access-Control-Allow-Origin: *. For production, specify the exact origin(s) allowed, e.g., Access-Control-Allow-Origin: https://www.your-frontend.com.
    • Why it works: This header is the primary signal to the browser that your API permits requests from the specified origin. * is a wildcard meaning "any origin."
  2. Incorrect Access-Control-Allow-Origin Value:

    • Diagnosis: Same as above, but check that the value provided exactly matches your frontend’s origin (protocol, domain, and port). For example, http://localhost:3000 is different from http://localhost:3000/.
    • Fix: Ensure the header value is precise. If your frontend is on https://app.example.com, set Access-Control-Allow-Origin: https://app.example.com. If you need to support multiple specific origins, you’ll need server-side logic to inspect the Origin header of the incoming request and dynamically set Access-Control-Allow-Origin to match.
    • Why it works: The browser performs an exact string comparison. A mismatch, no matter how small, leads to rejection.
  3. Missing Access-Control-Allow-Methods Header:

    • Diagnosis: The browser console might show an error like "Request method 'POST' not allowed by Access-Control-Allow-Methods in preflight response." This indicates the API didn’t specify which HTTP methods (GET, POST, PUT, DELETE, etc.) are allowed for cross-origin requests. This is particularly relevant for requests that are not "simple" (e.g., POST with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain).
    • Fix: Add Access-Control-Allow-Methods to your API’s response headers. List all HTTP methods your API supports for cross-origin requests, e.g., Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS.
    • Why it works: The browser checks this header during the CORS preflight (OPTIONS) request to ensure the actual request method is permitted.
  4. Missing Access-Control-Allow-Headers Header (for non-simple requests):

    • Diagnosis: Errors like "Request header field X is not allowed by Access-Control-Allow-Headers in preflight response." This happens when your frontend sends custom headers (e.g., Authorization, X-Requested-With, Content-Type if it’s not one of the simple types) in a cross-origin request.
    • Fix: Include Access-Control-Allow-Headers in your API’s response, listing the headers your frontend is sending, e.g., Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization.
    • Why it works: Similar to Allow-Methods, this header explicitly tells the browser which custom headers are acceptable for cross-origin requests.
  5. Missing Access-Control-Allow-Credentials Header (for cookies/auth):

    • Diagnosis: If your frontend needs to send credentials (like cookies or HTTP authentication) with a cross-origin request, and the API is not configured to accept them, the request will fail, often silently or with a generic console error about the request not being "loaded." The Access-Control-Allow-Origin header cannot be * when Access-Control-Allow-Credentials is true.
    • Fix: Add Access-Control-Allow-Credentials: true to your API’s response headers. Crucially, if you set this to true, you must set Access-Control-Allow-Origin to a specific, non-wildcard origin matching your frontend.
    • Why it works: This header explicitly authorizes the browser to include credentials in the cross-origin request. The browser enforces a security measure by disallowing * when credentials are involved.
  6. Incorrect CORS Preflight (OPTIONS request) Handling:

    • Diagnosis: For non-simple requests, the browser first sends an OPTIONS request to your API to check if the actual request is allowed. If your API doesn’t have a route to handle OPTIONS requests for your API endpoints, or if it doesn’t return the correct CORS headers in its OPTIONS response, the actual request will be blocked.
    • Fix: Implement an OPTIONS request handler for your API endpoints. This handler should return a 200 OK status and include all the necessary CORS headers (Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers).
    • Why it works: The preflight OPTIONS request acts as a handshake. If the handshake fails (e.g., no OPTIONS handler, or missing headers in the OPTIONS response), the browser aborts the process.
  7. Proxying Issues (e.g., Nginx, Cloudflare):

    • Diagnosis: If your API is behind a proxy, the proxy might be stripping or modifying the CORS headers sent by your actual API server.
    • Fix: Configure your proxy server to pass through or add the necessary CORS headers. For Nginx, this might involve add_header Access-Control-Allow-Origin ...; directives.
    • Why it works: Proxies can act as intermediaries, and without proper configuration, they might not forward or might overwrite the essential CORS headers.

After fixing these, you might encounter an error related to caching or an incorrect Vary header if your API is behind a CDN or load balancer.

Want structured learning?

Take the full API Architecture course →