Using X-Accel-Redirect in Nginx to Implement Controlled Downloads
1 Nov2006

Sometimes you may need to implement controlled downloads when all downloads requests being sent to your script and then this script decides what to do: to send some file to the user or to show some access denied page or, maybe, do something else. In lighttpd server it can be done by returning X-Sendfile header from script. Nginx have its own implementation of such idea using X-Accel-Redirect header. In this short post I will try to describe how to use this feature from PHP and Rails applications.

Lets assume, that you have some site and using Apache with PHP or Rails for dynamic content on this site. If you will use nginx as reverse-proxy in front of your Apache server, you will reach two goals:

  1. You would be able free some resources on server while nginx will handle all slow requests to dynamic content (details are here).
  2. You would be able to implement controlled downloads of static files from site.

In this article I will assume, that site is located in /var/www directory and there are some static files (like movies or song or something else) in /var/www/files directory. Apache is listening on http://127.0.0.1:8080.

First of all, lets take a look at our nginx configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
http {
    ....
    server {
        listen       80;
        server_name  your-domain.com;

        location / {
            rewrite ^/download/(.*) /down.php?path=$1 last;

            proxy_pass         http://127.0.0.1:8080/;
            proxy_redirect     off;

            proxy_set_header   Host             $host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

            client_max_body_size       10m;
            client_body_buffer_size    128k;

            proxy_connect_timeout      90;
            proxy_send_timeout         90;
            proxy_read_timeout         90;

            proxy_buffer_size          4k;
            proxy_buffers              4 32k;
            proxy_busy_buffers_size    64k;
            proxy_temp_file_write_size 64k;

        }

        location /files {
            root /var/www;
            internal;
        }
    }
}

As you can see, we have additional “internal” location /files there. This keyword “internal” allow us to have some locations, that will be available for user only in internal redirects and X-Accel-Redirect responses from backend scripts. So, we can use simple PHP script or Rails code on backend server to implement controlled downloads that will support Ranges header and all other features supported by direct static downloads from nginx servers.

Here is down.php script:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// Get requested file name
$path = $_GET["path"];

//...
// Perform any required security checks, validation
// and/or stats accounting
//...

// And redirect user to internal location
header("X-Accel-Redirect: /files/" . $path);

?>

In Rails applications you can use following code in your controller:

1
2
3
4
5
6
7
8
9
10
// Get requested file name
path = @params["path"]

# ...
# Perform any required security checks, validation
# and/or stats accounting
# ...

# And redirect user to internal location
@response.headers['X-Accel-Redirect'] = "/files/" + path

That’s it! With described approach we are able to create very flexible and extremely performance systems for file distribution!