CORS Headers: Request Permission and Response Exposure
By Ankit Jain · Updated
Know which CORS headers belong on OPTIONS, the actual response, or the browser’s request, with concrete examples.
CORS has two different header conversations: the browser describes the request it wants to send, and the server returns permission. Adding Access-Control-Allow-Origin to fetch’s request headers does not grant response access.
Header reference
| Header | Sent by | Purpose |
|---|---|---|
| Origin | Browser | Identifies the requesting origin |
| Access-Control-Request-Method | Browser on preflight | Names the intended actual method |
| Access-Control-Request-Headers | Browser on preflight | Names non-safelisted request headers |
| Access-Control-Allow-Origin | Server | Grants response access to one origin or an appropriate wildcard |
| Access-Control-Allow-Credentials | Server | Grants include-mode response access when exactly true |
| Access-Control-Allow-Methods | Server on preflight | Permits actual methods |
| Access-Control-Allow-Headers | Server on preflight | Permits request header names |
| Access-Control-Expose-Headers | Server on actual response | Makes additional response headers readable |
| Access-Control-Max-Age | Server on preflight | Allows preflight permission caching |
| Vary: Origin | Server | Tells HTTP caches that the response varies by Origin |
Allow-Headers and Expose-Headers differ
Suppose the frontend sends Authorization and needs to read X-Request-ID. The preflight response permits the request header:
Access-Control-Allow-Headers: Authorization
The actual response exposes the response header:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Expose-Headers: X-Request-ID
X-Request-ID: example-123
JavaScript can then read response.headers.get(‘X-Request-ID’). Ordinary safelisted response headers, such as Content-Type, are already readable when CORS permission succeeds. Set-Cookie remains unavailable to JavaScript.
A consistent policy matters
Return exactly one valid Allow-Origin value. For approved dynamic origins, use an allowlist and Vary: Origin; never return a list of origins. With credentials: include, use an exact origin, Allow-Credentials: true, and explicit method/header permissions where needed. Authorization always needs an explicit header name.
Put CORS headers on error responses that JavaScript needs to read as well as successes. Check duplicate headers and CDN caching when the application appears right but the browser sees something else.