Exposing headers on CORS responses

CORS and its discontents

The concept of CORS requests comes up a lot in my professional life. Much of the time, it's in the context of why a given response is opaque, and how to make that response non-opaque so that it plays nicely with service workers and the Cache Storage API.

Fortunately, many popular third-party APIs and hosts support CORS nowadays, and solving your basic CORS-related mystery normally boils down to, say, adding in the crossorigin attribute to your <img> tags.

When CORS is not enough

But while enabling CORS is enough to get back basic information about an HTTP response—like its status code, or access to its body—there's still some information that's locked down by default. The headers exposed on a CORS response, for instance, are limited to the following six "simple" response headers:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

Some of those headers can come in handy when accessed inside of a service worker, but there's one in particular that can be useful, but isn't exposed by default: Date.

In particular, if you're using Workbox's cache expiration logic and you provide a maxAgeSeconds parameter, the Date of the cached response is checked against the difference between the current time and maxAgeSeconds. If the Date is too old, then the cached response will end up being ignored.

But... this logic only works if there's a Date header exposed on the response. By default, that won't be the case for a CORS response.


The workaround, as with so many things related to CORS, involves fiddling with HTTP response headers. You'll either need access to the underlying HTTP server yourself, or you'll need to reach out to your CDN/API provider asking them to make the change.

Setting Access-Control-Expose-Headers: Date will permit the Date response header to be visible to your web app's code, and you could include any additional headers there in a comma-separated list.

If you're using your own Express-based web server, the corser middleware looks pretty reasonable for setting up a working configuration. Their docs include a recipe for configuring the exposed response headers.

Live demo

Here's a quick demonstration, separate from service workers, and using the fantastic https://httpbin.org service to control the Access-Control-Expose-Headers response header that's returned in a simulated API response. (They support CORS by default, so nothing needs to be done to enable that.)

Check out the log messages in the JavaScript console to see which headers are visible in the response.