Rails 5 allows setting custom HTTP Headers for assets

来源:互联网 时间:1970-01-01

October 31, 2015 - Vipul

Let’s look at by default what kind of response headers we get when we start with a brand new Rails 4.2.4 application.

Now let’s say that I want to set a custom response header. That’s easy. All I need to do is add following line of code in the controller.

response.headers['X-Tracking-ID'] = '123456'

Now I see this custom response header.

Setting custom respone header for assets

Let’s say that I need that custom response header not only for standard web requests but also for my assets. For example application.js file is served by Rails in localhost. How would I set custom header to the asset being served by Rails here.

Actually that’s not possible in Rails 4. In Rails 4 we can set only one response header for assets that are served by Rails and that response header is Cache-Control .

Here is how I can configure Cache-Control for assets.

# open config/environments/production.rb and add following lineconfig.static_cache_control = 'public, max-age=1000'

Now we have modified ‘Cache-Control` header for the assets.

However besides cache-control no other respone header can be set for assets served by Rails. That'a limitation.

How Rails apps lived with that limitation so far

Rails is not the best server to serve statis assets. Apace and NGINX are much better at this job. Hence in reality in production almost everyone puts either Apache or NGINX in front of a Rails server and in this way Rails does not have to serve static files.

Havig said that Rails applications hosted at Heroku is an exception. Assets for Rails applilcations running at Heroku are served by Rails application itself.

Problem we ran into

Ourwebsite is hosted at heroku. When we ran Google page speed insights on our website we were warned that we were not using Expiry header for the assets.

Here are how the headers look from response for such an asset.

Now you see the problem we are running into.

Our application is hosted at Heroku. Heroku lets Rails serve the assets. Google page speed insights wants us to set Expiry header to the assets. Rails application allows only one header for the assets and that is ‘Cache-Control`.

One easy solution is to host our website at Digital Ocean and then use Apache or NGINX.

Rails 5 saves the day

Recently Rails merged basic support for access control headers and added ability to define custom HTTP Headers on assets served by Rails.

Behind the scenes, Rails uses ActionDispatch::Static middleware to take care of serving assets. For example, a request to fetch an image, goes through ActionDispatch::Static in the request cycle. ActionDispatch::Static takes care of serving Rack::File object from server with appropriate headers set in the response. The serverd image can have headers like Content-Type , Cache-Control .

Start using Rails master

To fix this, we first pointed the App to use Rails master.

gem 'rails', github: 'rails/rails'gem 'rack', github: 'rack/rack' # Rails depends on Rack 2gem 'arel', github: 'rails/arel' # Rails master works alongside of arel master.

Next, we changed asset configuration to start providing and using missing header for Expires .

# production.rbconfig.public_file_server.headers = { 'Cache-Control' => 'public, s-maxage=31536000, maxage=15552000', 'Expires' => "#{1.year.from_now.to_formatted_s(:rfc822)}"}

Here, we are first setting the Cache-Control header to notify to use public (intermediate) caching, for a year (31536000 seconds) with maxage and s-maxage . Here s-maxage stands for Surrogate cache, which is used to cache internally by Fastly as well.

We then provide the missing Expires value with some future date, in Internet formatted time value.

With this setup, we can see PageSpeed pickup the new headers on assets and to not warn us on the missing headers-

Here are the changed response headers from the server, that now include the Expires header-

Further Reading

For better use and more details about different headers to use for the assets, please refer to RFC 2616 .