Nginx authenticated files access
by Stephane Robino on Tue, 7 Jul 2015When dealing with large files (2Gb+), we would prefer nginx to serve those files directly. The trick is we want nginx to serve those files solely to authenticated users. We will see in this post how to have nginx serve the files directly, yet keep the authorized access logic inside our web application.
Configuring Nginx
We want to allow users to download files at the following location: "/files/file_name/". file_name represents the file to download.
We start from a classic nginx proxy configuration proxying requests to our web application.
upstream proxy_app { server 127.0.0.1:7000; } server { listen 80; server_name example.com; rewrite_log on; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log debug; client_max_body_size 4G; keepalive_timeout 5; root /var/www; location / { # checks for static file, if not found proxy to app try_files $uri/index.html $uri.html $uri @forward_to_app; } location @forward_to_app { include /etc/nginx/proxy_params; proxy_pass http://proxy_app; } }
The idea here is to add an internal directive to URLs for private files and a couple proxy headers.
$ diff -u prev /etc/nginx/conf.d/default ... + location /files/(.*)/ { + internal; + alias /path/to/your/private/files/$1/; + } location @forward_to_app { include /etc/nginx/proxy_params; proxy_pass http://proxy_app; + proxy_set_header X-Sendfile-Type X-Accel-Redirect; + proxy_set_header X-Accel-Mapping /path/to/your/private/files/=/files/(.*)/; ...
Configuring the Web application
Django
We were serving PDF files through Django here. To have the files served by
nginx instead, we return an HttpResponse
with an empty body
and add a X-Accel-Redirect
header. For example:
... response = HttpResponse() response['Content-Type'] = 'application/pdf' response['Content-Disposition'] = 'attachment; filename=%s.pdf' % (file_name, ) response['X-Accel-Redirect'] = '/files/%s' % (file_name, ) return response
Rails
Working with Rails is about the same though a bit different.
All the magic happens in the
Rack::sendfile
middleware. We only need to configure the appropriate x_sendfile_header
then call send_file as usual.
In config/environments/production.rb:
# Set header to use. config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
In the controller:
# In case of PDF file send_file( path, :disposition => 'inline', :type => "application/pdf")
More to read
If you are looking for more posts dealing with large files over HTTP, Browser direct upload to S3 is worth reading next. If you are looking into using nginx as a proxy, you might also enjoy Nginx / Gunicorn / Django.
More technical posts are also available on the DjaoDjin blog, as well as business lessons we learned running a SaaS application hosting platform.