Expose HTTP Headers in CORS
For APIs, it seems more and more data is making its way into the HTTP headers. Recently, whilst adding a Link
header, for paging data, into the HTTP response, I realized that my browser code couldn't see it. Who was the culprit? CORS, of course.
Headers in Browser
It was just another day, and I was developing in my browser of choice, Chrome, known, in part, for its unequaled developer tools. The Link
header that I had added onto my HTTP responses for my API were viewable from the Network tab. It looked good. The browser could see it, curl could even see it.
In the browser client code, I was using superagent
, but the code failed to see it. I'd run:
var request = require('superagent')
request.get('myApi').end(function (err, res) {
res.xhr.getResponseHeader('Link') // nothing
res.headers.link // nothing
})
And the header was not coming through. I ran the same command in the console, res.xhr.getResponseHeader('Link')
, and got the error response:
Refused to get unsafe header ‘Link’
These are a classic CORS symptoms.
CORS Spec
Well, a few great google results later, I ended up at the CORS spec, which drones on that only simple headers are allowable in CORS requests by default. These headers include:
- Cache-Control
- Content-Language
- Content-Type
- Expires
- Last-Modified
- Pragma
Link
is definitely not on that list. So, how to add it?
Access-Control-Expose-Headers in rack-cors
Like the other CORS headers, the Access-Control-
prefix is present. The one you're looking for is Access-Control-Expose-Headers
. It is a comma-separated list of header names. You can add this header manually to your response, or depending on the library you're using, it might be added differently.
I was using rack-cors
. Thus, my addition ended up looking something like this in config/application.rb
:
# ...
config.middleware.use Rack::Cors do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :put, :delete, :options], :expose => ['Link']
end
end
Can you see that the expose
key was the key for me? Once that was added, the browser let the client code read the link, and all was safe and well.
Bonus:
As a bonus, here's a great client library for consuming a GitHub API-style Link
header, called parse-link-header