Serving Static Files with Passenger and Nginx
The X-Accel-Redirect HTTP header is used on Nginx servers to allow applications to serve static files directly from Nginx. For those familiar with X-Sendfile, X-Accel-Redirect is the Nginx implementation. Using this header to serve static files from a Rails application has some great benefits, like not requiring a file to be streamed through the Passenger and Rails stacks - which can be costly. The setup is quite simple.
-
Create a controller to serve the file.
class BlogsController < ApplicaitonController def show @blog = Blog.find(params[:id]) response.headers["X-Accel-Redirect"] = @blog.path response.headers["Content-Type"] = "application/json" render :nothing => true end end
- Write an nginx location block into nginx.conf. server { ... location /blogs/ { internal; root /data; } }
-
Serve the file.
blog = Blog.create(:path => "/blogs/my_first_blog.json") uri = URI.parse("http://localhost:3000/blogs/#{blog.id}.json") Net::HTTP.get uri
The request goes something like this:
- The HTTP GET request goes to Nginx, Passenger, and finally to your Rails BlogsConroller#show action.
- The BlogsController#show action responds with two HTTP headers (X-Accel-Redirect and Content-Type).
- The X-Accel-Redirect header is trapped on the return trip by Nginx.
- Nginx matches the X-Accel-Redirect header with the location /blogs/ block.
- Nginx serves the file directly from /data/blogs/my_first_blog.json.
- Nginx also persists the Content-Type HTTP header and passes it to the client.
An additional benefit of this method is that you can use the controller to check file permissions. For example:
class BlogsController < ApplicaitonController
def show
@blog = Blog.find(params[:id])
if current_user.can_access?(@blog)
response.headers["X-Accel-Redirect"] = @blog.path
response.headers["Content-Type"] = "application/json"
render :nothing => true
else
redirect_to permission_denied
end
end
end