Access-Control-Expose-Headers — Making Response Headers Available to JavaScript
In this tutorial, you will learn about Access. We cover key concepts, practical examples, and best practices to help you master this topic.
The Access-Control-Expose-Headers response header tells the browser to make specific response headers available to JavaScript, overriding the default set of seven simple response headers.
What You'll Learn
- Which headers are exposed by default
- How to expose custom headers to JavaScript
- Security implications of exposing headers
Why It Matters
APIs commonly use custom headers for rate limits, pagination, request tracing, and ETags. Without Expose-Headers, JavaScript cannot read these values. DodaTech's API uses custom headers for rate limit status, and the client SDK reads them via Expose-Headers.
flowchart LR
A["Server Response"] --> B{"Header in Expose-Headers?"}
B -->|"Yes"| C["JavaScript can read it"]
B -->|"No"| D["Is it a simple header?"]
D -->|"Yes"| C
D -->|"No"| E["JavaScript cannot read it"]
style C fill:#86efac,stroke:#16a34a
style E fill:#fecaca,stroke:#dc2626
Code Examples
// Without Expose-Headers, custom headers are hidden
fetch('https://api.example.com/data')
.then(response => {
// These work - they are simple response headers
console.log(response.headers.get('content-type'));
console.log(response.headers.get('last-modified'));
// These return null without Expose-Headers
console.log(response.headers.get('x-rate-limit-remaining'));
console.log(response.headers.get('x-total-count'));
console.log(response.headers.get('x-request-id'));
});
# Flask response with exposed headers
@app.route('/api/users')
def get_users():
users = get_users_from_db()
response = jsonify(users)
response.headers['X-Rate-Limit-Remaining'] = '97'
response.headers['X-Total-Count'] = '1000'
response.headers['X-Request-ID'] = 'req-abc-123'
response.headers['Access-Control-Expose-Headers'] = \
'X-Rate-Limit-Remaining, X-Total-Count, X-Request-ID'
response.headers['Access-Control-Allow-Origin'] = '*'
return response
// Node.js exposing headers for pagination
app.get('/api/items', (req, res) => {
const items = getItems(req.query.page, req.query.limit);
res.set('X-Page', req.query.page || '1');
res.set('X-Total-Pages', Math.ceil(totalItems / limit));
res.set('X-Total-Items', String(totalItems));
res.set('Access-Control-Expose-Headers',
'X-Page, X-Total-Pages, X-Total-Items');
res.json(items);
});
// Client reads pagination headers
const response = await fetch('https://api.example.com/items');
const totalPages = response.headers.get('X-Total-Pages');
const currentPage = response.headers.get('X-Page');
# Check exposed headers
curl -I -H "Origin: https://app.example.com" \
https://api.example.com/data | grep -i "expose-headers"
Common Mistakes
1. Confusing Expose-Headers with Allow-Headers
Allow-Headers controls request headers. Expose-Headers controls response headers.
2. Not Exposing Authentication-Related Headers
Headers like WWW-Authenticate require explicit exposure.
3. Exposing Sensitive Headers
Avoid exposing internal headers like X-Internal-Token or X-Debug-Info.
4. Forgetting to Expose CORS-Related Headers
If you set custom headers like X-CSRF-Token, expose them so JavaScript can read new values.
5. Assuming All Response Headers Are Visible by Default
Only six simple headers are visible: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma.
Practice Questions
- Which six response headers are visible without Expose-Headers?
- How do you expose the X-Total-Count header?
- Why would an API expose custom headers?
- Can you use a wildcard in Access-Control-Expose-Headers?
- What happens if JavaScript tries to read a non-exposed header?
Answers:
- Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma.
- Set Access-Control-Expose-Headers: X-Total-Count in the response.
- To allow client code to read pagination, rate limits, or request tracing information.
- Yes, modern browsers support the wildcard * for Expose-Headers.
- The browser returns null for that header.
Challenge: Refactor an API to move all relevant metadata from the response body into custom headers, expose them properly, and update the client SDK to read from headers instead of the body.
FAQ
Mini Project
Build an API that returns paginated results with all metadata in headers: X-Page, X-Per-Page, X-Total, X-Total-Pages. Properly expose them, then build a JavaScript client that reads pagination state from headers and displays page navigation. Include error handling when headers change between requests.
What's Next
Explore Access-Control-Allow-Credentials to handle cookies and authentication headers cross-origin, then study Access-Control-Max-Age for preflight Caching.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro