Should I have these directives in my Nginx config? - ruby-on-rails

I'm using just Rails straight with Nginx (no apache), and wondering if I even need the following in my virtual hosts:
# serve static content directly
location ~* \.(ico|jpg|gif|png|css|js|swf|html)$ {
if (-f $request_filename) {
expires max;
break;
}
}
Is it even relevant if I'm not using Apache?
Thanks in advance.

Since you are using Rails, probably the only config you need is just:
server {
listen 80;
server_name example.com;
location / {
root path/to/static/files;
try_files $uri #rails;
expires max;
}
location #rails {
# proxy_pass to gunicorn or whatever...
}
}
But it is always better to keep all media files in a separate directory:
server {
listen 80;
server_name example.org;
location / {
# proxy_pass to gunicorn or whatever...
}
location /media/ {
root path/to/static/files;
try_files $uri #rails;
expires max;
}
}
Leave ugly and complicated rules like:
location ~* \.(ico|jpg|gif|png|css|js|swf|html)$`
to PHP-folks. They usually have a mess of code and media in their projects and like programming in web-server configs instead of doing all the application logic in application.

This if is meaningless and just wasting of your cpu.
From documentation:
Enables or disables adding or modifying the “Expires” and “Cache-Control” response header fields provided that the response code equals 200, 201, 204, 206, 301, 302, 303, 304, or 307.
# http://nginx.org/r/expires
Do you see 404 here? I do not.
Stops processing the current set of ngx_http_rewrite_module directives.
# http://nginx.org/r/break
And I see no other rewrite directives in your location.

This directive tells nginx to set the expires header to largest possible value and to stop processing further directives when requested file ends with one of those extensions and exists. Those files will be served by nginx. This is valid directive whether you use apache or not. You don't have to use it, but you better do because nginx is very good at serving static files.
Update
As other people pointed out, you should avoid using if. You can do this by:
location ~* \.(ico|jpg|gif|png|css|js|swf|html)$ {
try_files $uri =404;
expires max;
}
if you want to do special processing, use named locations:
location ~* \.(ico|jpg|gif|png|css|js|swf|html)$ {
try_files $uri #process_404;
expires max;
}
location #process_404{
# do your stuff
}

Related

Nginx + Puma + Sidekiq web interface not showing css styles

I have an angularJS web app running in a Nginx server that sends request to a Rails API running in a Puma server. I have integrated Sidekiq 5.2.8 and everything works great but the Sidekiq web interface.
In my Nginx config file, I have a rule to pass request to the API. Please find the whole nginx.conf document:
events {
worker_connections 1024;
}
http {
upstream api.development {
# Path to Puma SOCK file, as defined previously
server unix:/tmp/puma.sock fail_timeout=0;
}
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# set client body size to 10M #
client_max_body_size 10M;
gzip on;
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
#charset koi8-r;
#access_log logs/host.access.log main;
root /Users/Rober/Projects/domain/dev/domain/app;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
# Proxy requests to the backoffice Rails API
location /api {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
#proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
rewrite ^/api(.*) /$1 break;
proxy_pass http://api.development;
}
# Rule to proxy the sidekiq web UI
location /sidekiq {
proxy_pass http://api.development;
}
# Expire rules for static content
# RCM: WPO
# Images
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
root /Users/Rober/Projects/domain/dev/domain/app;
expires 1w;
add_header Cache-Control "public";
}
# This rule is the root cause of the problems with the sidekiq css
# I have commented it for testing purposes
# CSS and Javascript
#location ~* \.(?:css|js)$ {
# root /Users/Rober/Projects/domain/dev/domain/app;
# expires 1w;
# add_header Cache-Control "public";
#}
# I have replaced the previous location above for this as suggested by #Beena Shetty.
location ~* \.(?:css|js)$ {
add_header X-debug-message "Into the location css" always;
if ($uri !~* "^/sidekiq/\w*(.*)+$") {
add_header X-debug-message "Into the location css if" always;
root /Users/Rober/Projects/domain/dev/domain/app;
expires 1w;
add_header Cache-Control "public";
}
}
# cache.appcache, your document html and data
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
root /Users/Rober/Projects/domain/dev/domain/app;
expires -1;
}
}
include servers/*;
}
In Rails:
routes:
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'
I have included next rule in Nginx config file and now when I request http://localhost/sidekiq I can see the web interface and navigate, but still cannot see the styles.
location /sidekiq {
proxy_pass http://api.development;
}
See screenshot.
The dev tools shows that when I load sideqik is trying to get bootstrap.css and some other css and javascript in http://localhost/sidekiq/stylesheets/bootstrap.css
What am I missing?
UPDATE:
I have found out the root cause of the problem in my nginx.conf. I have next rule that set a cache expiration time for performance purposes. If I comment this code, everything works. But how can I have both things living together?
CSS and Javascript
location ~* \.(?:css|js)$ {
root /Users/Rober/Projects/domain/dev/domain/app;
expires 1w;
add_header Cache-Control "public";
}
UPDATE 2: Just in case the problem comes from another point, I have included my whole nginx conf.
Now, with the provided config, the expiration rules in my web app are still working, but the css in the sidekiq webapp do not.
I have included two headers as debug. One when the server is accessing the location rule and the second when the server is accessing inside the if condition. When I request my home page with localhost and I check the request for my own css, such as app.css, I can see the header X-debug-message: Into the location css if, which is right.
If I request sidekiq with localhost/sidekiq I still get 404 error for css, let´s say http://localhost/sidekiq/stylesheets/bootstrap.css and I can see the header X-debug-message: Into the location css.
Current conclusions:
As soon as I include the location ~* .(?:css|js)$ rule, sidekiq css stops working. Even if the rule is empty, like:
location ~* \.(?:css|js)$ {
}
As soon as I delete or comment the whole rule, the sidekiq css works perfectly, but unfortunately this is not compatible with the expires rules that we need to include for performance purposes.
Try this:
location ~* \.(?:css|js)$ {
if ($uri !~* "^/sidekiq/\w*(.*)+$"){
root /Users/Rober/Projects/domain/dev/yanpy/app;
expires 1w;
add_header Cache-Control "public";
}
}
I wasn't able to find a fix for this, so I hacked it with the following method: I copied sidekiq's assets to the public folder and it started working ( since they're referenced by the UI ).
|- images
|-- favicon.io
|-- logo.png
|-- status.png
|- javascripts
|-- application.js
|-- dashboard.js
|- stylesheets
|-- application.css
|-- application-rtl.css
|-- application-dark.css
|-- application-rtl.min.css
|-- bootstrap.css
Mainly, these files: https://github.com/mperham/sidekiq/tree/master/web/assets

nginx: use different server for different path

I have two Rails applications sitting behind two different URIs on the same machine. The nginx configuration is for each application in an own configuration file.
Now one of the applications has to be slowly merged into the second one. There is an endpoint accessible under the URI path application-one.com/register. I want only this application-one.com/register path to be accessing the second application and all other paths (application-one.com/* except /register) still accessing the first application.
Is there a way to do this without using 301 Redirects since I am not able to use them in my case?
An example configuration file looks like this:
upstream unicorn_application_one {
server unix:/tmp/unicorn_application_one.sock fail_timeout=0;
}
server {
client_max_body_size ....
...
server_name application-one.com
root /home/deployer/application_one/current/public;
try_files $uri/index.html $uri #unicorn_application_one;
location #unicorn_application_one {
proxy_set_header ...
....
}
location ^~ /assets/ {
...
}
....
}
EDIT:
I tried #joaumg approach but I had to change it:
Whenever I extracted an upstream to an external file I got a duplicate upstream error.
So I just changed the location from #joaumg's code to:
location /register {
proxy_pass http://unix:/tmp/unicorn_application_two.sock;
}
Is there a way to do it in the way #joaumg is telling without having a duplicate upstream error?
A possible example (untested):
cat upstreams.conf
upstream unicorn_application_one {
server unix:/tmp/unicorn_application_one.sock fail_timeout=0;
}
upstream unicorn_application_two {
server unix:/tmp/unicorn_application_two.sock fail_timeout=0;
}
cat server_one.conf
include upstreams.conf;
server {
client_max_body_size ....
...
server_name application-one.com
root /home/deployer/application_one/current/public;
location /register {
proxy_pass #unicorn_application_two;
}
try_files $uri/index.html $uri #unicorn_application_one;
location #unicorn_application_one {
proxy_set_header ...
....
}
location ^~ /assets/ {
...
}
....
}

Nginx fall-through urls with caching

I have Rails app that has routes matching static files that are generated on first access.
Everything works fine if I have this block commented out in my site.conf:
location ^~ /uploads/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
Is there a way to have best of both worlds and have location block to be only activated if the actual file exists and fall-through if it isn't? Maybe add try inside?
One of best practices to serve response based on logic "local disk static file vs. dynamic response with backend" is try_files:
location ^~ /uploads/ {
gzip_static on;
expires max;
add_header Cache-Control public;
try_files $uri #backend;
}
location #backend {
proxy_pass ...
}
See official docs here.

HTTP_IF_MODIFIED_SINCE header not passed to Rails app (Nginx, Passenger)

I am trying to use the HTTP_IF_MODIFIED_SINCE header in my app to determine if resources are stale/fresh and render 200/304 in those cases.
In my dev environment everything works fine but I can't for the life of me get it to work in production.
I am using Passenger 3.0.11 and Nginx 1.0.13.
As you see below, I tried proxy_pass_header, proxy_set_header, passenger_pass_header and passenger_set_cgi_param. The last one actually sets a HTTP_IF_MODIFIED_SINCE header but it is empty...
Any help/ideas would be greatly appreciated!
My config:
server {
listen 80 default_server;
root /home/rails/myapp/current/public;
passenger_enabled on;
charset utf-8;
proxy_pass_header If-Modified-Since;
proxy_set_header If-Modified-Since $http_if_modified_since;
passenger_pass_header If-Modified-Since;
passenger_set_cgi_param HTTP_IF_MODIFIED_SINCE $http_if_modified_since;
if (-f $document_root/system/maintenance.html) {
rewrite ^(.*)$ /system/maintenance.html break;
}
location ~ \.(aspx|jsp|cgi)$ {
return 410;
}
location ~ ^/(assets)/ {
# http://guides.rubyonrails.org/asset_pipeline.html#server-configuration
# gzip_static on;
expires 1y;
add_header Cache-Control public;
add_header Last-Modified "";
add_header ETag "";
break;
}
}
to get it working with non-standard headers, containing underscores, do this inside your http or server block in the nginx.conf file:
underscores_in_headers on;
ignore_invalid_headers off;
and in the server block:
proxy_pass_header HTTP_IF_MODIFIED_SINCE
This can be useful if you have legacy HTTP headers which you need to deal with, and which contain underscores.
This was a user error after all. I sent the header to the app in the wrong format (IF_MODIFIED_SINCE instead of If-Modified-Since). After fixing that, it worked without any of the extra directives.

nginx rewrite rules with Passenger

I'm trying to migrate to nginx from Apache using Passenger in both instances to host a Rails app. The app takes a request, which is for an image- if the image exists at /system/logos/$requestedimage then it should get served, or it should be allowed to hit the Rails app to generate it if needed (where it is then cached to /system/logos).
In Apache I used the following:
RewriteCond %{DOCUMENT_ROOT}/system/logos/%{REQUEST_FILENAME} -f
RewriteRule ^/(.*)$ http://assets.clg.eve-metrics.com/system/logos/$1
This worked fine. The assets. subdomain is another subdomain but with the same root, just Passenger disabled, specifically set up for hosting static files (expires-wise).
In nginx I am using the following:
server {
listen 80;
passenger_enabled on;
server_name clg.eve-metrics.com www.clg.eve-metrics.com;
root /opt/www/clg/current/public;
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain application/xml text/css application/javascript;
gzip_disable msie6;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
if (-f $document_root/system/logos$request_filename) {
rewrite ^/(.*)$ http://assets.clg.eve-metrics.com/system/logos/$1 break;
}
}
This doesn't work so well. At all, in fact. It never redirects to the cached path and it never hits the Rails app. It's like nginx is assuming it's a static asset so not passing it on to Passenger. Is there a way to stop this behaviour so it hits the app?
My rails application is running on nginx and passenger. I have moved my rails cache directory from the default /public to /public/system/cache/. To make it work, I had to insert this into my vhost config file:
if (-f $document_root/system/cache/$uri/index.html) {
rewrite (.*) /system/cache/$1/index.html break;
}
if (-f $document_root/system/cache/$uri.html) {
rewrite (.*) /system/cache/$1.html break;
}
I remember that I too tried to make it work with $request_filename, but didn't get it to work. Try with $uri instead and see if it works :-)
James, please try this configuration file
https://gist.github.com/711913
and pay attention on this location config:
location ~* \.(png|gif|jpg|jpeg|css|js|swf|ico)(\?[0-9]+)?$ {
access_log off;
expires max;
add_header Cache-Control public;
}
passenger won't let Rails to manage your assets files if you have right permissions (user run nginx should has permissions to access to file directly)

Resources