RAILS SERVE STATIC FILES with NGINX

By Ronak Gothi On September 04 2019

Rails 4.2+ has introduced an environment variable in production ENV['RAILS_SERVE_STATIC_FILES'] which disallows serving static files and assets by default.

1
2
3
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?

Assets generated during precompilation task rake assets:precompile like Images, CSS, JS, robot.txt, sitemap.xml, favicon etc are not served by rails in production.

PaaS platform like Heroku automatically sets ENV['RAILS_SERVE_STATIC_FILES'] = true there by providing a seamless deploy experience. However, deploying the app on a custom linux server behind the NGINX would result in the following image


Rails assets not loading in production

Solution

To fix the above problem there are two options here -

  1. Enable Rails to serve static assets by setting ENV['RAILS_SERVE_STATIC_FILES'] to true
  2. Configure NGINX to serve the static files/assets. The request never really reaches Rails.

Rails serve static assets - Options

Nginx configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  upstream your_app {
    server unix:///var/www/your_app/shared/tmp/sockets/puma.sock fail_timeout=0;
  }
  server {
    listen 443 ssl;
    root /var/www/your_app/current/public;
    server_name your-app.com www.your-app.com;

    location / {
      resolver 8.8.8.8;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto https;
      proxy_redirect off;
      proxy_set_header Host $host;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
      proxy_pass http://your_app;
     }

    location ~ ^/(assets|packs)/ {
      gzip_static on;
      expires max;
      add_header Cache-Control public;
      try_files $uri =404;
      error_page 404 /404.html;
    }

    location ~ ^(?!/rails/).+\.(jpg|jpeg|gif|png|ico|json|txt|xml)$
      gzip_static on;
      expires max;
      add_header Cache-Control public;
      try_files $uri =404;
      error_page 404 /404.html;
    }
 }


Performance and Benchmark

How does these method compare against each other? Let's do a load test

NGINX served assets -

1
2
3
4
5
6
7
8
9
10
11
12
Transactions:               5411 hits
Availability:             100.00 %
Elapsed time:              59.21 secs
Data transferred:           3.68 MB
Response time:              0.54 secs
Transaction rate:          91.39 trans/sec
Throughput:                 0.06 MB/sec
Concurrency:               49.72
Successful transactions:    5411
Failed transactions:           0
Longest transaction:        0.92
Shortest transaction:       0.31

Rails served assets -

1
2
3
4
5
6
7
8
9
10
11
12
Transactions:               5009 hits
Availability:             100.00 %
Elapsed time:              59.80 secs
Data transferred:           3.41 MB
Response time:              0.59 secs
Transaction rate:          83.76 trans/sec
Throughput:                 0.06 MB/sec
Concurrency:               49.66
Successful transactions:    5009
Failed transactions:           0
Longest transaction         0.80
Shortest transaction:       0.07

NGINX offers better transaction rate by 9.1%, moreover Rails Server is freed up to do it's real job. This setup can be further be improved by using nginx proxy_cache directive.