Before Rails 5,
returning false from any before_ callback
in ActiveModel or ActiveModel::Validations,
ActiveRecord and ActiveSupport
resulted in halting of callback chain.
In this case the code is
attempting to set the value of eligibility_for_rebate to false.
However the side effect of the way Rails callbacks work is that
the callback chain will be halted
one of the callbacks returned false.
Right now, to fix this we need to return true
from before_ callbacks,
so that callbacks are not halted.
Improvements in Rails 5
Rails 5 fixed this issue
by addingthrow(:abort) to
explicitly halt callbacks.
Now, if any before_ callback returns false
then callback chain is not halted.
To explicitly halt the callback chain,
we need to use throw(:abort).
Opting out of this behavior
The new Rails 5 application comes up
with initializer named callback_terminator.rb.
helps in managing gem dependencies of ruby projects.
You can specify which gems and versions you need, bundler will install them and load them at runtime.
Bundler ensures that gems you need are present in the environment you
Bundler gets its configurations from local application (app/.bundle/config), environment variables and user’s home directory (~/.bundle/config) in the order of priority.
To list all bundler configurations for the current bundle, run bundle config without any parameters. You will also get the location where the value is set.
You might see different result based on configuration of bundler on your
To get value for the specific configuration setting, run bundle config with name.
To set the value of the configuration setting, use bundle config with name and value. Configuration will be stored in ~/.bundle/config.
If any config already has a value, it will be overwritten directly and user will be warned.
Application level configuration
By default setting configuration value will set it for all projects on the machine. You can set configurations specific to local application with --local option. This will store value in app/.bundle/config.
You can run bundle config with --global.
This will set values at global level i.e. across all applications on the machine.
It will be similar to running bundle config without any options.
You can delete configuration with --delete option.
This is not compatible with --local and --global. This will delete configuration from local and global resources.
You can pass the flags required for installing particular gem to bundler with bundle config.
Many El Capitan users face an
while installing eventmachine gem.
The issue can be resolved by providing path to OpenSSL include directory while installing eventmachine.
As location of configuration will vary from machine to machine, we can set this with bundle config.
Now bundler will pick this configuration while installing eventmachine gem.
Various configuration keys are available with bundler.
These keys are available in two forms.
You can specify them in canonical form with bundle config or set them in environment variable form.
Following are canonical forms and their usage.
Corresponding environment variables are specified in bracket.
Setting auto_install config will enable automatic installing of gems instead of raising an error.
This applies to show, binstubs, outdated, exec, open, console, license, clean commands.
Example, When you try to run bundle show for the gem which is not yet installed you will get an error.
You can set auto_install to remove this error and install gem.
You can specify location to install your gems. Default path is $GEM_HOME for development and vendor/bundle when –deployment is used.
You can freeze changes to your Gemfile.
If frozen is set and you try to run bundle install with changed Gemfile, you will get following warning.
You can skip installing groups of gems with bundle install. Specify : separated group names whose gems bundler should not install.
You can set the directory to install executables from gems in the bundle.
You can set the file which bundler should use as theGemfile. By default, bundler will use Gemfile. The location of this file also sets the root of the project, which is used to resolve relative paths in the Gemfile.
This specifies path to a designated CA certificate file or folder containing multiple certificates for trusted CAs in PEM format. You can specify your own https sources in Gemfile with corresponding certificates specified via bundle config.
This specifies path to a designated file containing a X.509 client certificate and key in PEM format.
You can set the path to place cached gems while running bundle package.
When set, Gemfiles containing multiple sources will produce an error instead of a warning.
When you try to run bundle install, you will get warning.
To ignore all bundle config on the machine and run bundle install, set BUNDLE_IGNORE_CONFIG environment variable.
Often while developing a Rails application you may look to have one of these
to boost the performance.
Along with these, Rails 5 now provides a way of caching a collection of records,
thanks to the introduction of the following method:
What is collection caching?
Consider the following example where we are fetching a collection of all users belonging to city of Miami.
Here @users is a collection of records and is an object of class ActiveRecord::Relation.
Whether the result of the above query would be same depends on following
The query statement doesn’t change. If we change city name from
“Miami” to “Boston” then result might change.
No record is deleted. The count of records in the collection should be same.
No record is added. The count of records in the collection should be same.
implemented caching for a collection of records .
Method cache_key was added to ActiveRecord::Relation which
takes into account many factors including
query statement, updated_at column value and the count of the records in collection.
We have object @users of class ActiveRecord::Relation.
Now let’s execute cache_key method on it.
Let’s try to understand each piece of the output.
users represents what kind of records we are holding.
In this example we have collection of records of class User.
Hence users is to illustrate that we are holding users records.
query- is hardcoded value and it will be same in all cases.
is a digest of the query statement that will be executed.
In our example it is MD5( "SELECT "users".* FROM "users" WHERE "users"."city" = 'Miami'")
3 is the size of collection.
is timestamp of the most recently updated record in the
collection. By default, the timestamp column considered is updated_at
hence the value will be the most recent updated_at value in the collection.
Let’s see how to use cache_key to actually cache data.
In our Rails application, if we want to cache records of users belonging to “Miami”
then we can take following approach.
From above, we can see that
for the first hit,
a count query is fired to get the
the latest updated_at and size from the users collection.
Rails will write a new cache entry with a cache_key
generated from above count query.
Now on second hit,
it again fires count query and
checks if cache_key for this query exists or not.
If cache_key is found, it loads data without firing SQL query.
What if your table doesn’t have updated_at column?
Previously we mentioned that cache_key method uses
updated_at column. cache_key also provides an option of
passing custom column as a parameter
then the highest value of that column among the records in the collection will be considered.
For example if your business logic considers
a column named last_bought_at in products table as a factor to decide caching, then you can use the following code.
Edge cases to watch out for
Before you start using cache_key there are some edge cases to watch
Consider you have an application where there are
5 entries in users table with city Miami.
Using limit puts incorrect size in cache key if collection is not loaded.
If you want to fetch three users belonging to city “Miami” then you would execute following query.
Here users contains only three records
hence the cache_key has 3 for size of collection.
Now let’s try to execute same query without fetching the records first.
You can see that the count in the cache is 5 this time even though we have set a limit to 3.
This is because the implementation of
executes query without limit
to fetch the size of the collection.
Cache key doesn’t change when an existing record from a collection is replaced
I want 3 users in the descending order of ids.
Above statement will give us
users with ids [5, 4, 3].
Now let’s remove the user with id = 3.
Note that cache_key both users1 and users2 is exactly same.
This is because none of the parameters that affect the cache key is changed
i.e., neither the number of records,
nor the query statement,
nor the timestamp of the latest record.
a discussion undergoing
about adding ids of the collection records as part of the cache key.
This might help solve the problems discussed above.
Using group query gives incorrect size in the cache key
Just like limit case discussed above cache_key behaves differently
when data is loaded and when data is not loaded in memory.
Let’s say that we have two users with first_name “Sam”.
First let’s see a case where collection is not loaded in memory.
In the above case, the size is 1 in cache_key.
For the system mentioned above, the sizes that you will get shall either be 1 or 5.
That is, it is size of an arbitrary group.
Now let’s see when collection is first loaded.
In the above case, the size is 2 in cache_key.
You can see that the count in the cache key here
is different compared to that where the collection was unloaded
even though the query output in both the cases will be exactly same.
In case where the collection is loaded,
the size that you get is equal to the total number of groups.
So irrespective of what the records in each group are,
we may have possibility of having the same cache key value.
The advantage is that we do not need to restart the
server manually if we want to turn caching “on” or “off”.
It is internally taken care by the dev_cache method
that is executed when rails dev:cache is executed.
You can see in the source code that tmp/restart.txt is being touched.
Please note that this feature is not supported by unicorn, thin and
webrick. My guess is that DHH wants this feature because his team uses
pow and pow restarts when tmp/restart.txt is touched.
He also created an issue for
spring to watch tmp/restart.txt
Disabling development cache
Execute the same command that was used to enable caching. If caching was
previously enabled then it will be turned “off” now.
By default PostgreSQL is configured to be bound to “localhost”.
As we can see above port 5432 is bound to 127.0.0.1. It means any
attempt to connect to the postgresql server from outside the machine will be refused.
We can try hitting the port 5432 by using telnet.
In order to fix this issue we need to find postgresql.conf. In
different systems it is located at different place. I usually search for
Open postgresql.conf file and replace line
Now restart postgresql server.
Here we can see that “Local Address” for port 5432 has changed to 0.0.0.0.
Let’s try to connect to remote postgresql server using “psql”.
In order to fix it, open pg_hba.conf and add following entry at the
The second entry is for IPv6 network.
Do not get confused by “md5” option mentioned above. All it means is
that a password needs to be provided. If you want client to allow
collection without providing any password then change “md5” to “trust”
and that will allow connection unconditionally.
Restart postgresql server.
You should be able to see list of databases.
Now we are able to connect to postgresql server remotely.
Please note that in the real world you should be using extra layer of
security by using “iptables”.
In Rails 4 some commands start with rails and some commands start with
rake. This could be quite confusing for people new to Rails. Let’s see
Our task is to write a database migration and then to run that migration.
Above command creates a migration. Now we need to run that migration.
As you can see first command starts with rails and second command
starts with rake.
In order to consolidate them we can either use rails for everything or
we can use rake for everything.
Choosing rails command over rake command
Some favor using rake over rails.
But an important feature missing in Rake
is the ability to pass arguments.
In order to execute the above command using rake
we will have to pass console and development
as arguments. We can pass these values using environment variables.
That would mean adding additional code in Rake task to fetch right
values and then only we will be able to invoke command
rails console development.
Rails 5 enables executing rake commands with rails
Rails core team
to have consistency by enabling rails command to support
everything that rake does.
For example in Rails 5 commands like db:migrate, setup, test etc which are part of rake command in Rails 4 are
now being supported by rails command.
However you can still choose to use rake to run those commands similar to how
they were run in Rails 4.
This is because Rails community has
introduced Rake Proxy
instead of completely moving the command options from rake to rails.
What happens internally is that when rails db:migrate command is executed, Rails checks if
db:migrate is something that rails natively supports or not.
In this case db:migrate is not natively supported by rails,
so Rails delegates the execution to Rake via Rake Proxy.
If you want to see all the commands that is supported by rails in
Rails 5 then you can get a long list of options by executing rails
Use app namespace for framework tasks in Rails 5
As rails command is now preferred
over rake command,
few rails namespaced framework tasks
started looking little odd.
So, Rails team decided to change the namespace
for these tasks from rails to app.
Using rails rails:update will now give deprecation warning like:
DEPRECATION WARNING: Running update with the rails: namespace is deprecated in favor of app: namespace. Run bin/rails app:update instead.
More improvements in pipeline
In Rails 4, the routes are usually searched like this.
Since nested forms are not allowed browser will accept the top most
level form. In this case that happens to be the form created by the
hacker. When this form is submitted then “authenticity_token” is also
submitted and Rails will do its check and will say everything is looking
good and thus hacker will be able to hack the site.
Rails 5 fixes the issue by generating a custom token for a form
Rails request-response cycle is very easy to understand.
A request hits the app, a route is matched to a controller action from routes.rb,
and finally controller action processes the request and renders HTML or JSON based
on the type of the request.
But sometimes we want to render our HTML or JSON response outside of this request-response
For example let’s say user is allowed to download PDF version of a
report on web. This can be done using request-response cycle. We also
need to send a weekly report to managers and the email should have the
report as an attachment. Now we need to generate the same PDF but since
emails are sent using background job the request-response cycle is
Rails 5 has this feature baked in.
Let’s say we have a OrdersController and
we want to render individual order outside of controller.
Fire up rails console and execute following command.
This will render app/views/orders/show.html.erb with @order set to
Order.last. Instance variables can be set using
assigns in the same way
we use them in controller actions.
Those instance variables will be passed to
the view that is going to be rendered.
Rendering partials is also possible.
This will render app/views/orders/_form.html.erb and will pass order as local
Say I want to render all orders, but in JSON format.
Even rendering simple text is possible.
Similar to text, we can also use render file and render template.
A typical web request carries it’s own environment with it. We usually handle
this environment using request.env in controllers.
Certain gems like devise depends on env hash for information such as warden token.
So when we are rendering outside of controller,
we need to make sure that
the rendering happens with correct environment.
Rails provides a default rack environment for this purpose.
The default options used
can be accessed through renderer.defaults.
Internally, Rails will build a new Rack environment based on these options.
Customizing the environment
We can customize environment using method renderer.
Let’s say that we need “method as post” and we want
“https to be true” for our background job processing.
Now that we have our custom renderer we can use it to generate view.
Overall this is a nice feature which enables reuse of existing code.
Recently we came across following error
when mobile app was trying to upload images using backend API.
The backend API was developed using Ruby on Rails and was powered using NGINX HTTP server along with unicorn application server.
Notice the the http response received was 502 which is documented in
RFC as shown below.
10.5.3 502 Bad Gateway
The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request.
Debugging the problem
From the first look it seemed that the client_max_body_size parameter for nginx was too less,
which might cause the request to fail if the uploaded filesize is greater than the allowed limit.
But that was not the case with our NGINX configuration.
The NGINX was configured to accept file size upto 250 megabytes, while the file being uploaded was around 6 megabytes.
In our Rails code we are forcing all traffic to use HTTPS by having production.rb to have following setting.
What this setting does is, it forces HTTPS by redirecting HTTP requests to their HTTPS counterparts.
So any request accessing http://domain.com/path will be redirected to https://domain.com/path
Forcing all traffic to HTTPS still does not explain the vague error sendfile() failed (32: Broken pipe) while sending request to upstream and why the HTTP 502 error which means Bad Gateway
Here is how our nginx.conf is configured.
NGINX is configured to accept both HTTP and HTTPS requests. This is a
pretty standard setting so that application can support both
HTTP and HTTPS.
What it means is that nginx will accept both HTTP and HTTPS request.
It would not force HTTP request to be HTTPS.
It would forward HTTP request to rails backend api.
Here is what happens when you upload a file
When we upload a file then NGINX takes that request and passes that
request to Rails. NGINX expects a response from Rails only when
NGINX is done sending the whole request.
Let’s see what happens when a user visits login page using HTTP. NGINX
takes the request and passes the request to Rails. When NGINX is done
handing over the request to Rails then NGINX expects a response from
Rails. However in this case rather than sending 200 Rails sends a
redirect over HTTPS. So now NGINX takes up the request again over HTTPS
and then hands over request to Rails. Rails processes the request and
returns 200. Everything works out.
Now let’s see what happens when a file is uploaded over HTTP.
User uploads a file over HTTP. NGINX takes up the request and starts
sending the file to Rails. More details about the file upload process
can be see at RFC. The data is
sent to server in chunks. When first chunk is sent to server then server
notices that data is being sent over HTTP and it immediately sends a
response that data should be sent over HTTPS.
In the meantime on the client side , client is still pushing rest of the
data to the server. Client expects a response from the server only when
it is done pushing all the data. However in this case server sends a
response when client is not expecting it.
NGINX at this time is all confused and thinks something has gone wrong
with the gateway and returns HTTP/1.1 502 Bad Gateway and aborts the
upload operation. And that how we get the 502 error.
So next time you see an error like this make sure that you are using
https to upload the file if server is enforcing https.
We at BigBinary adopted ReactJS pretty early. Early in the year we
published series of videos titled Learn ReactJS in steps
which takes a “Hello World” app into a full TODO application using
ReactJS in incremental steps.
If you run bin/rails -h in a Rails 5 app, you will see a new command for running tests.
Before Rails 5, we had to use bin/rake test to run tests.
But in Rails 5, we can use bin/rails test.
It is not just replacement of old rake task but it is backed by a test runner
inspired from RSpec, minitest-reporters, maxitest and others.
Let’s see what bin/rails test can do.
Running a single test
Now it is possible to run a single test out of the box using the
line number of the test.
Rails will intelligently run the test from user_test.rb having line number 27.
Note that the line number 27 does not need to be first line of the test.
Below is an example.
In the above case, test_valid_user can be run as long as the line number
provided is between 22 and 30.
Running multiple tests
You can also pass multiple test paths and Rails will run all of those tests.
It is also possible to run all tests from a directory.
Improved failure messages
When a test fails, Rails displays a command which can be used to just run
the failed test.
I can simply copy
bin/rails test test/controllers/posts_controller_test.rb:15
and rerun the failing test.
By default when a test fails then rails reports about the test failures
and then moves on to the next test. If you want to stop running the test
when a test fails then use option -f.
Defer test output until the end of the full test run
By default when a test fails then rails prints F and then details
about the failure like what assertion failed and how to re run the test
If you want to have a clean output of . and F and would like all the
test failures report to come at the every end then use option -d.
Better backtrace output
By default when an error is encountered while running the test
then the output does not contain full stacktrace.
This makes debugging little bit difficult.
Now we can use -b switch,
which will display complete backtrace of error message.
Leveraging power of Minitest
The test runner also leverages power of minitest by providing some handy options.
Switch -s to provide your own seed
Now we can also provide our own seed using -s switch.
Switch -n to run matching tests
Switch -n will run tests matching
the given string or regular expression pattern.
It is also possible to see the verbose output using -v switch.
It shows time required to run each test.
This would help in detecting slow running tests.
Now by default we will get colored output.
No need to add additional gem to colored output.
With all these awesome features, testing Rails 5 apps has definitely become
a better experience.
Rails has shipped all these features within
the framework itself so you don’t have to use multiple gems and libraries to achieve
all of these things.
Upto Rails 4.2, all models inherited from ActiveRecord::Base.
But starting from Rails 5, all models will inherit from ApplicationRecord.
What happened to ActiveRecord::Base ?
Well not much changed in reality. Following file will be automatically added to
models in Rails 5 applications.
This behavior is similar to how controllers inherit from ApplicationController
instead of inheriting from ActionController::Base.
Now ApplicationRecord will be a single point of entry for all the customizations and
extensions needed for an application, instead of monkey patching ActiveRecord::Base.
Say I want to add some extra functionality to Active Record.
This is what I would do in Rails 4.2.
But now, ActiveRecord::Base forever includes MyAwesomeFeature and any class
inheriting from it also includes MyAwesomeFeature even if they don’t want it.
This is especially true if you are using plugins and engines where monkey patching to ActiveRecord::Base
can leak into engine or plugin code.
But with ApplicationRecord, they will be localized to only those models which
are inheriting from ApplicationRecord, effectively only to your application.
Migrating from Rails 4
By default all new Rails 5 applications will have application_record.rb.
If you are migrating from Rails 4, then simply create app/models/application_record.rb as shown below
and change all models to inherit from ApplicationRecord instead of ActiveRecord::Base.
When a Rails application involving multiple gems, engines etc. are built then it’s important
to know how constants are looked up and resolved.
Consider a brand new Rails app with model User.
Run User.model_method in rails console. It runs as expected.
Now add file user.rb in lib directory.
Reload rails console and try executing User.model_method and User.lib_method.
You will notice that User.model_method gets executed and User.lib_method doesn’t. Why is that?
In Rails we do not import files
If you have worked in other programming languages like Python or Java
then in your file you must have statements to import other files. The
code might look like this.
In Rails we do not do that. That’s because
does not like the idea of opening a file and seeing the top of the file
littered with import statements.
He likes to see his files beautiful.
Since we do not import file then how does it work?
In Rails console when user types User then rails detects that User
constant is not loaded yet.
So it needs to load User constant.
However in order to do that it has to load a file.
What should be the
name of the file. Here is what Rails does.
Since the constant name is User Rails says that I’m going to look for file user.rb.
So now we know that we are looking for user.rb file. But the
question is where to look for that file. Rails has autoload_path. As
the name suggests this is a list of paths from where files are
automatically loaded. Rails will search for user.rb in this list of
Open Rails console and give it a try.
As you can see in the result one of the folders is app/models. When
Rails looks for file user.rb in app/models then Rails will find it
and it will load that file.
That’s how Rails loads User in Rails console.
Adding lib to the autoload path
Let’s try to load User from lib directory.
Open config/application.rb and add following code in the
Now exit rails console and restart it.
And now lets try to execute the same command.
Here you can see that lib directory has been added at the very top.
Rails goes from top to bottom while looking for user.rb file. In this
case Rails will find user.rb in lib and Rails will stop looking for
user.rb. So the end result is that user.rb in app/models directory
would not even get loaded as if it never existed.
Enhancing a model
Here we are trying to add an extra method to User model. If we stick
our file in lib then our user.rb is never loaded because Rails will
never look for anything in lib by default. If we ask Rails to look in
lib then Rails will not load file from app/models because the file
is already loaded. So how do we enhance a model without sticking code in
Introducing initializer to load files from model and lib directories
We need some way to load User from both models and lib directories.
This can be done by adding an
initializer to config/initializers directory with following code snippet
Now both User.model_method and User.lib_method get executed as expected.
In the above case when first time user.rb is loaded then constant
User gets defined. Second time ruby understands that constant is
already defined so it does not bother defining it again. However it adds
additional method lib_method to the constant.
In that above case if we replace load file with require file then
User.lib_method will not work. That is because require will not load
a file if a constant is already defined. Read
to learn about how load and require differ.
Using ‘require_relative’ in model
Another approach of solving this issue is by using require_relative inside model. require_relative loads the file
present in the path that is relative to the file where the statement is called in. The desired file to be loaded is given
as an argument to require_relative
In our example, to have User.lib_method successfully executed, we need to load the lib/user.rb. Adding the following code
in the beginning of the model file user.rb should solve the problem. This is how app/models/user.rb will now look like.
Here require_relative upon getting executed will first initialize the constant User from lib directory.
What follows next is opening of the same class User that has been initialized already and addition of model_method to it.
Handling priorities between Engine and App
In one of the projects we are using engines.
SaleEngine has a model Sale. However Sale doesn’t get resolved as
path for engine is neither present in config.autoload_paths nor in ActiveSupport::Dependencies.autoload_paths.
The initialization of engine happens in engine.rb file present inside lib directory of the engine.
Let’s add a line to load engine.rb inside application.rb file.
In Rails console if we try to see autoload path then we will see that
lib/sale_engine is present there. That means we can now use
Now any file we add in sale_engine directory would be loaded. However
if we add user.rb here then the user.rb mentioned in app/models
would be loaded first because the application directories have
precedence. The precedence order can be changed by following statements.
The symbol :main_app refers to the application where the server comes up. After adding the above code, you will see that
the output of ActiveSupport::Dependencies now shows the directories of engines first (in the order in which they have been
given) and then those of the application. Hence for any class which is common between your app and engine, the one from
engine will now start getting resolved. You can experiment by adding multiple engines and changing the railties_order.
Loading of constants is a big topic and Xavier Noria from Rails core team has made some excellent presentations. Here are some of them
I added stripe to my React Native application using following command.
Added Stripe to the project as shown below.
And now I’m getting following error.
This’s because npm package uses
provided by Node.JS.
Our React Native code does not actually run on Node.JS.
I know while building React Native
we use NPM
we have a node server running in the background
so it feels like
we are in Node.JS land
we can use
everything Node.JS has to offer.
But that’s not true.
React Native code can’t depend on Node.JS
Node.JS is not shipped with ios.
we can’t use any of Node.JS packages including http.
we can’t use stripe npm module.
Now you can remove
require('stripe') line that was added
in the last step.
React Native comes with
From the documentation
such as requests and responses. It also provides a global fetch() method that provides an easy,
logical way to fetch resources asynchronously across the network.
stripe.js is a nice wrapper around stripe api.
stripe api is very well documented
is easy to use.
Since we can’t use stripe.js
use stripe api.
Here, the header has 3 keys. Lets go through them one-by-one:
'Accept': 'application/json' : Designates the content to be in JSON format.
'Content-Type': 'application/x-www-form-urlencoded' : This tells the endpoint that the payload will be one giant query string where name and value pairs will be separated by the ampersand.
'Authorization': 'Bearer ' + '<YOUR-STRIPE-API-KEY>' : This key is to authorize our actions on Stripe. Here Bearer is just a prefix which we need to attach to the api-key because Stripe uses OAuth 2.0 . You can read more on the Bearer token usage here .
Payload needs to contain credit card details.
Since the Content-Type is application/x-www-form-urlencoded,
the payload should be one query string.
An example of this would be: city=Miami&state=Florida
Let’s prepare a proper payload.
Now we can attach formBody to
the body part of the fetch request
we are good to go.
Here’s the whole code snippet.
This is an example of
registering a credit card with stripe
getting the token.
Similar implementations can be done for other API endpoints.
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.
Now I see this custom response header.
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.
Now we have modified ‘Cache-Control` header for the assets.
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.
Havig said that, Rails applications hosted at Heroku is an exception.
Assets for Rails applications running at Heroku are 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.
Next, we changed asset configuration to start providing and using missing header for Expires.
Here, we are first setting the Cache-Control header 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.
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.
Here is the changed response header for asset.
For better use and more details about different headers to use for the assets, please refer to