Parse CORS HTTP Response Headers
Cross-Origin Resource Sharing (CORS) is the browser security mechanism that controls which web origins can make requests to your API. When a React frontend on app.example.com calls an API on api.example.com, the browser first checks whether the API permits this cross-origin request by looking at the CORS headers in the response. If the headers are missing or incorrect, the browser blocks the response and the developer sees a CORS error in the console — but the server logs show a 200 response, creating the maddening situation where the API works fine from curl but fails from the browser. This example shows the full set of CORS response headers for an API that allows requests from a specific origin. Access-Control-Allow-Origin: https://app.example.com whitelists exactly this one origin — note that this is an explicit origin, not a wildcard. Access-Control-Allow-Methods lists the HTTP methods the API permits for cross-origin requests. Access-Control-Allow-Headers lists the request headers the client is allowed to send — any custom header (like Authorization or X-Request-ID) must be explicitly listed here or the browser blocks it. Access-Control-Allow-Credentials: true is the most nuanced header in this set. It enables the browser to include cookies and authorization headers in cross-origin requests. However, it has a strict requirement: when credentials are enabled, Access-Control-Allow-Origin cannot be the wildcard (*). It must be an explicit origin. This pairing is one of the most common CORS misconfiguration bugs — developers set credentials to true but keep the wildcard origin, and the browser rejects the response. Access-Control-Max-Age: 86400 caches the preflight OPTIONS response for 24 hours. Without this, complex requests (those with custom headers or non-simple methods) trigger a preflight OPTIONS request before every actual API call, doubling the number of network round-trips. Caching the preflight result for 86400 seconds means the browser only sends one preflight per day per endpoint per origin. The Vary: Origin response header is important for caching correctness: if your server sends different Access-Control-Allow-Origin values for different requesting origins (reflecting the actual request origin rather than using a wildcard), the Vary: Origin header tells CDNs and browser caches not to serve a cached CORS response from one origin to a different origin. Real-world debugging workflow: when a CORS error occurs, open DevTools Network tab, find the failing request, and check two things: was there a preflight OPTIONS request, and what does the response headers section show? Missing CORS headers mean the server didn't handle the CORS request at all (often a routing issue). Present but incorrect headers point to a configuration problem. Tips: in development, use a proxy configuration in your bundler (Vite, webpack) to forward API requests from localhost to your backend, eliminating CORS issues entirely during development without weakening your production CORS policy.
Access-Control-Allow-Origin: https://app.example.com Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization, X-Request-ID Access-Control-Allow-Credentials: true Access-Control-Max-Age: 86400 Vary: Origin Content-Type: application/json; charset=utf-8 X-Content-Type-Options: nosniff
FAQ
- Why does CORS block requests with credentials when using a wildcard origin?
- Browsers reject CORS responses that combine Access-Control-Allow-Credentials: true with Access-Control-Allow-Origin: * because allowing credentials to any origin would be a serious security risk.
- What does Access-Control-Max-Age do?
- It caches the preflight OPTIONS response for the specified number of seconds, reducing the overhead of preflight requests for complex CORS calls.
- How do I debug a CORS error in the browser?
- Open DevTools Network tab, find the failing request, and look at the response headers. The actual CORS headers (or their absence) tell you exactly what the server is or is not allowing.
Related Examples
Content Security Policy (CSP) is the most powerful browser-enforced defense agai...
Nginx SSL and Security ConfigurationA production Nginx SSL configuration is more than just pointing Nginx at a certi...
Decode a JWT Authentication TokenJSON Web Tokens (JWTs) are the backbone of modern authentication in REST APIs, O...