Static Files With Nginx + Passenger

Normally, when you setup Nginx and Passenger to serve your rails application, all static files are served by Nginx without hitting Passenger. What really happens is that all static files that do exist are served by Nginx and the rest are passed on to passenger.

For example:

http://www.mydomain.com/javascripts/prototype.js

will be served by Nginx, but

http://www.mydomain.com/javascripts/iamastupidrobot.js

will continue to Passenger going all the way through the Rails stack. Instead of a simple 1ms nginx process , you get a full stack rails request with a route recognition error. Not good.

Missing or outdated images (specially if your site is big on content) or stupid robots searching for certain .php (or .js, or _vti_bin(?) or whatever) vulnerable files is common place. Why would you want those requests to pass on to Passenger?

Using an asset host

One solution is to use an asset host for all static files by using an absolute url like:

<script type="text/javascript" src="http://assets.mydomain.com/javascripts/prototype.js"></script>

instead of

<script type="text/javascript" src="/javascripts/prototype.js"></script>

Then you can make a separate entry for nginx for assets.mydomain.com with just a root directive to your public directory. This way static files will always be served by nginx, even the missing ones.

In addition you can specify multiple asset hosts and tell Rails to cycle through them. This is especially good for splitting the load among multiple servers and bypassing the 2 requests per ip browser constraint. What I don’t like with this approach is that simple image tags have to go through the image_tag helper, causing some overhead for no good reason.

Configure Nginx

set $asset F;

if ($request_filename ~* ".*\.(gif|jpg|css|jpeg|png|bmp|asp|php|_vti_bin|js)$") {
    set $asset T;
}

if (!-f $request_filename) {
    set $asset "${asset}T";
}

if ($asset = TT) {
    return 404;
    break;
}