Access-Control-Expose-Headers: reading response headers
Access-Control-Expose-Headers names the response headers that page code may read on a cross-origin response. Everything outside the default safelist is hidden until it is listed.
At a glance
- Direction
- Response, on the actual response rather than the preflight.
- Defined in
- The Fetch standard (WHATWG)
- When absent
- Only the safelisted response headers are readable. Everything else appears to be missing.
- Readable by default
- Cache-Control, Content-Language, Content-Length, Content-Type, Expires, Last-Modified, Pragma
- Commonly exposed
- Location, ETag, Link, Retry-After, X-Request-Id and pagination headers
- Wildcard
- Supported for non-credentialed requests only
- Where it belongs
- The real response. Putting it only on the preflight has no effect.
In detail
The header does not change what the browser receives, only what page code can see. The full response, headers included, arrives intact; the fetch layer then hides anything outside the safelist, so a header you can see clearly in the network panel reads as null from JavaScript. That gap between the tooling and the code is why this one takes so long to diagnose.
The default safelist is short and static: cache and content descriptors plus a couple of dates. Everything a modern API actually wants to expose falls outside it, including ETag for conditional requests, Location after a create, Link for pagination, Retry-After on a rate limited response, and any correlation id you want a client to log.
It belongs on the real response. A preflight carrying the header changes nothing, because the browser reads it when it makes the response available to the caller. That is the opposite arrangement from the allow methods and allow headers lists, which only matter on the preflight.
The wildcard is available without credentials and is the pragmatic choice for a genuinely public API. With credentials it stops matching, so a session backed API has to enumerate the headers it wants readable, and any header not on that list becomes invisible to the very client that needs it.
Examples
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Expose-Headers: Link, X-Total-Count, X-Request-Id
Link: </items?page=3>; rel="next"
X-Total-Count: 148Access-Control-Expose-Headers: *The wildcard is ignored for credentialed requests, so a cookie backed API has to list its headers by name.
Things that catch people out
- A header visible in the browser network panel can still read as null from page code. The panel shows what arrived, not what CORS permits you to read.
- ETag is not on the default safelist, so cross-origin conditional requests silently stop working until the tag is exposed.
- The wildcard stops matching as soon as credentials are involved, which turns a working public endpoint into a broken authenticated one.
Try it: HTTP Headers Checker
Check a live URL or paste headers you already have. It grades six security headers from A+ to F and lists every other header the response returned.
HTTP Headers Checker
LOCAL · Browser-OnlyView HTTP response headers for any URL
Enter any website URL to fetch and analyze its HTTP response headers. We'll automatically add https:// if needed.
Why Check Security Headers?
Security headers protect your website against common attacks like XSS, clickjacking, and MIME sniffing. They're set by your web server and cost nothing to implement. Missing security headers are one of the most common findings in security audits.
Related HTTP headers
All http headers →Access-Control-Allow-Origin: who may read this response
How the allow origin header is matched, why a wildcard fails with credentials, why it takes only one origin, and why Vary: Origin is mandatory when you echo.
Access-Control-Allow-Credentials: cookies across origins
What credentials mode means, the three wildcards it disables, what the client must also do, and why this header needs an exact origin in the response.
ETag: version identifiers for conditional requests
How an ETag is compared, when to use a weak tag, how it drives 304 responses and 412 conflicts, and why compression changes the tag a server sends.
Link: relationships expressed in a header
How the Link header carries relationships that markup cannot, its use for preload with early hints, and the pagination pattern that APIs build on it.
Access-Control-Max-Age: caching the preflight result
How long a browser may reuse a preflight result, the caps Chrome and Firefox apply, what minus one means, and why a doubled request count is normal.
Origin: where a request was initiated from
When browsers send Origin, why it is only scheme, host and port, when the value is null, and how to use it for cross-site request forgery defence.
Tools for this
- HTTP Request Builder Free online HTTP request builder - build and test HTTP requests with custom headers and body
- HTTP Headers Checker Free online HTTP headers checker - view HTTP response headers for any URL