---
title: "Rails 5 allows setting custom HTTP Headers for assets"
description:
  "Ability to set custom HTTP header is needed to have better control over the
  delivery of assets. In this blog we'll see how that can be done."
canonical_url: "https://www.bigbinary.com/blog/rails-5-allows-setting-custom-http-headers-for-assets"
markdown_url: "https://www.bigbinary.com/blog/rails-5-allows-setting-custom-http-headers-for-assets.md"
---

# Rails 5 allows setting custom HTTP Headers for assets

Ability to set custom HTTP header is needed to have better control over the
delivery of assets. In this blog we'll see how that can be done.

- Author: Vipul
- Published: October 31, 2015
- Categories: Rails 5, Rails

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.

![header](https://www.bigbinary.com/blog/images/images_used_in_blog/2015/rails-5-allows-setting-custom-http-headers-for-assets/header1.png)

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.

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

Now I see this custom response header.

![header](https://www.bigbinary.com/blog/images/images_used_in_blog/2015/rails-5-allows-setting-custom-http-headers-for-assets/header2.png)

## Setting custom response 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.

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

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

![header](https://www.bigbinary.com/blog/images/images_used_in_blog/2015/rails-5-allows-setting-custom-http-headers-for-assets/header3.png)

Besides `Cache-Control` no other response header can be set for assets served by
Rails. That's a limitation.

## How Rails Apps lived with this limitation so far

Rails is not the best server to serve static 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.

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

### Problem we ran into

Our [website](https://bigbinary.com) is hosted at heroku. When we ran
[Google page speed insights](https://developers.google.com/speed/pagespeed/insights/)
for our website we were warned that we were not using `Expires` header for the
assets.

![PageSpeed Insights Warning](https://www.bigbinary.com/blog/images/images_used_in_blog/2015/rails-5-allows-setting-custom-http-headers-for-assets/PageSpeedInsightsWarnings.png)

Here are how the header looked for `application.js`.

![PageSpeed Insights Warning](https://www.bigbinary.com/blog/images/images_used_in_blog/2015/rails-5-allows-setting-custom-http-headers-for-assets/bigbinary_before_expires.png)

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 `Expires` 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](https://www.digitalocean.com) and then use Apache or NGINX.

## Rails 5 saves the day

Recently Rails
[merged basic support for access control headers](https://github.com/rails/rails/pull/19135)
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 served 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.

```ruby

gem 'rails', github: 'rails/rails'
gem 'rack', github: 'rack/rack' # Rails depends on Rack 2
gem '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`.

```ruby
# production.rb

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

```

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

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

With this setup, we can see PageSpeed pickups the new headers on assets and does
not warn us for the missing header.

![PageSpeed Insights Warning](https://www.bigbinary.com/blog/images/images_used_in_blog/2015/rails-5-allows-setting-custom-http-headers-for-assets/PageSpeedInsightsSolved.png)

Here is the changed response header for asset.

![PageSpeed Insights Warning](https://www.bigbinary.com/blog/images/images_used_in_blog/2015/rails-5-allows-setting-custom-http-headers-for-assets/bigbinary_after_expires.png)

## Further Reading

For better use and more details about different headers to use for the assets,
please refer to
[RFC 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html).

## Links

- [Human page](https://www.bigbinary.com/blog/rails-5-allows-setting-custom-http-headers-for-assets)
