Fix CORS Expose Headers for Custom Response Headers
In this tutorial, you'll learn about Fix CORS Expose Headers for Custom Response Headers. We cover key concepts, practical examples, and best practices.
Your frontend code calls response.headers.get('X-Total-Count') or a similar custom header, but the value is null. The CORS spec only exposes six safe response headers by default. All custom headers require explicit opt-in via Access-Control-Expose-Headers.
Wrong
The server sends a custom header but does not expose it to the client.
Server sends:
X-Total-Count: 247
X-RateLimit-Remaining: 58
Frontend code:
fetch('https://api.example.com/users')
.then(res => {
console.log(res.headers.get('X-Total-Count'));
console.log(res.headers.get('X-RateLimit-Remaining'));
});
null
null
The custom headers exist on the wire but the browser hides them from JavaScript because Access-Control-Expose-Headers is not set.
Right
Add the Access-Control-Expose-Headers header with the names of the custom headers you want the frontend to read.
const cors = require('cors');
app.use(cors({
origin: 'https://app.example.com',
exposedHeaders: ['X-Total-Count', 'X-RateLimit-Remaining', 'X-Request-Id'],
}));
Manual implementation:
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'https://app.example.com');
res.setHeader('Access-Control-Expose-Headers',
'X-Total-Count, X-RateLimit-Remaining, X-Request-Id');
if (req.method === 'OPTIONS') return res.sendStatus(204);
next();
});
Response headers now include:
Access-Control-Expose-Headers: X-Total-Count, X-RateLimit-Remaining, X-Request-Id
X-Total-Count: 247
X-RateLimit-Remaining: 58
Frontend code now reads:
fetch('https://api.example.com/users')
.then(res => {
console.log(res.headers.get('X-Total-Count'));
console.log(res.headers.get('X-RateLimit-Remaining'));
});
247
58
Prevention
- List every custom response header in
Access-Control-Expose-Headersthat the frontend needs to read. - Use the
Access-Control-Expose-Headerswith a comma-separated list of header names. - Remember that
Cache-Control,Content-Language,Content-Length,Content-Type,Expires, andLast-Modifiedare exposed by default. - Add pagination headers (
X-Total-Count,X-Page,X-Per-Page) to the expose list when building list APIs. - Add rate limit headers (
X-RateLimit-Remaining,X-RateLimit-Reset) for client-side throttling. - Debug missing headers by checking the Network tab in DevTools to confirm the headers reach the browser.
DodaTech Tools
Doda Browser displays both exposed and hidden response headers in its developer tools, with warnings for headers the server sends but does not expose. The browser's network inspector also shows a separate section for non-exposed headers. DodaZIP's API documentation generator automatically reads and documents all exposed headers from the CORS configuration. Durga Antivirus Pro exposes its threat-score headers through CORS for its web dashboard to display real-time risk metrics, including X-Threat-Level, X-Scan-Duration, and X-Malware-Confidence.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro