Don't Forget to Expose Headers When Using Rack CORS

This week I was building an api on Rails and I did implement a cache system based on ETags.

You have a client (mobile application in my case) which needs to fetch data from the server. Most likely you only want to download the payload when something has changed since the last download.

This is the perfect scenario for using ETags.

You set a header (ETag specifically) with a key that identifies the content being returned. The client gets and saves the key. In the next request, the client sends it through If-None-Match header.

If the coming key in If-None-Match is different from the one generated in current response's content, then the server sends new data back to client, otherwise, it returns a 304 Not Modified status meaning that the content is unchanged.

The aforementioned steps can be reproduced using curl (or any other library):

Request:

curl -i http://localhost:3000/api/v1/customers

Response (check the ETag header):

HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
ETag: "e5ef2b0d51f78edeeba33c95c486ga28"
Content-Type: application/json; charset=utf-8
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 59118bbb-5bd8-4d86-8537-1fdff40dfda0
X-Runtime: 0.068550
Transfer-Encoding: chunked

[{"id":5,"name":"John"...

Now, let's send to the server the gotten ETag.

Request:

curl -i -H "If-None-Match: e5ef2b0d51f78edeeba33c95c486ga28" http://localhost:3000/api/v1/customers

Response:

HTTP/1.1 304 Not Modified
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
ETag: "e5ef2b0d51f78edeeba33c95c486ga28"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 4aed9148-6902-45d5-ba73-941644d22d39
X-Runtime: 0.071959

Ok, nothing new so far. What is the matter?

You see that in the curl's response we are getting the ETag header as expected.

However, the mobile app I am building is hybrid, so, I need to access cross-origin resources. The Rack CORS gem help us in such task.

After installing the gem, you just need to set the config in any initializer, like below:

Rails.application.config.middleware.insert_before 0, "Rack::Cors" do
allow do
origins '*'
resource '*', headers: :any, methods: [:get, :post, :options, :patch, :delete]
end
end

The matter is, by default, Rack CORS does not expose any headers except content-type and cache-control.

If your client needs to read another header (like ETags in this case), you need explicitly expose it:

Rails.application.config.middleware.insert_before 0, "Rack::Cors" do
allow do
origins '*'
resource '*', headers: :any, methods: [:get, :post, :options, :patch, :delete], expose: ['ETag']
end
end

Having it in mind may save your time.

See you.

Written on January 20, 2016

Share: