How Rails, Nginx and X-Accel-Redirect work together
TL;DR - To use X-Accel-Redirect to serve static files from e.g /mnt/filestorage using send_file, put this in your nginx server configuration.
proxy_set_header X-Accel-Mapping /mnt/filestorage/=/private_files/;
location /private_files/ {
internal;
alias /mnt/filestorage/;
}The need for X-Accel-Redirect (and it’s sibling X-Sendfile) comes from two distinct requirements
- The need to deliver large files.
- The need for those files to not be available to the public.
Here we are going to see how to set up X-Accel-Redirect and Rails. This is a bit complex so I’m going to run through a specific example of downloading a file. Along the way we’ll see the configuration, code and HTTP headers that are used. This assumes you already have Rails and Nginx installed and working.
1. Browser makes a request for a file
# HTTP Headers
GET /download/SecretSquirrel.zip HTTP/1.12. Nginx receives this request. It adds on a header with configuration data that will be required by rails.
#Nginx configuration
proxy_set_header X-Accel-Mapping /mnt/filestorage/=/private_files/;
# Example HTTP Headers with additional header added by nginx
GET /download/SecretSquirrel.zip HTTP/1.1
X-Accel-Mapping: /mnt/filestorage/=/private_files/3. Nginx passes the request onto Rails and it invokes the relevant controller.
4. The controller makes its authorization checks and calls send_file. Use the absolute path to the file.
# controller code (e.g. app/controllers/downloads_controller.rb)
send_file('/mnt/filestorage/SecretSquirrel.zip')5. Rails (Rack to be precise) then decides what to with the file. We need to tell rails to use X-Accel-Redirect in its configuration as shown below. Instead of using the file as the body of the request, it will add a header to the response. It uses the X-Accel-Mapping that nginx added earlier to change the file path.
# Rails configuration (e.g. config/environments/production.rb)
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
# HTTP Response
HTTP/1.1 200 OK
X-Accel-Redirect: /private_files/SecretSquirrel.zip
Content-Type: application/octet-stream
Content-length: ...
Content-Disposition: attachment; filename="SecretSquirrel.zip"
<empty body>6. Nginx receives this header from rails and interprets it. It finds the location directive and reverses the changes to the path that rails made in step 5.
#Nginx configuration
location /private_files/ {
internal;
alias /mnt/filestorage/;
}
# HTTP Response
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: ...
Content-Disposition: attachment; filename="SecretSquirrel.zip"
<contents of /mnt/filestorage/SecretSquirrel.zip>7. Browser receives the file as if it was a normal download.
Also, if you have compiled passenger into nginx, remember to use passengersetcgiparam instead of proxyset_header
Software Versions used
- Rails 3.0.7
- Passenger 3.0.5
- Nginx 0.8.54
- on Ubuntu 10.04.2 LTS
Resources
- Rack::Sendfile documentation
- Nginx X-Accel documemtation
- More documentation on Nginx’s X-Accel-Redirect
Trackbacks
Use the following link to trackback from your own site:
http://thedataasylum.com/trackbacks?article_id=33