Andrew Timberlake Andrew Timberlake

Hi, I’m Andrew, a programmer and entrepreneur from South Africa,
building Mailcast for taking control of your email.
Thanks for visiting and reading.


How to protect downloads but still have nginx serve the files

I’ve just been working on a project where a number of downloads needed to be restricted to specific users. I needed to authenticate the user and then allow them access to the file. This is not too difficult in rails:

def download
  if authenticated?
    send_file #{RAILS_ROOT}/downloads/images/myfile.zip'
  end
end

The problem with this is that if the file is large, rails will spend a lot of time sending this file to the browser. The solution, hand it off to the webserver (in my case, nginx) to send the file once the authentication has succeeded. nginx supports a header named X-Accel-Redirect. Using this header, you send a full path to the file to be downloaded:

def download
  if authenticated?
    #Set the X-Accel-Redirect header with the path relative to the /downloads location in nginx
    response.headers['X-Accel-Redirect'] = '/downloads/myfile.zip'
    #Set the Content-Type header as nginx won't change it and Rails will send text/html
    response.headers['Content-Type'] = 'application/octet-stream'
    #If you want to force download, set the Content-Disposition header (which nginx won't change)
    response.headers['Content-Disposition'] = 'attachment; filename=myfile.zip'
    #Make sure we don't render anything
    render :nothing => true
  end
end

You will need to add a location directive in nginx marked as internal which nginx will use along with your path to get to the physical file.

location /downloads {
  root /rails_deploy/current/downloads;
  #Marked internal so that this location cannot be accessed directly.
  internal;
}

Notes:

You can also set additional control using the following headers:

X-Accel-Limit-Rate: 1024
X-Accel-Buffering: yes|no
X-Accel-Charset: utf-8

See the nginx documentation on X-Accel-Redirect for more information.

1 Dec 2010