Использование X-Accel-Redirect с Nginx для реализации контролируемых скачиваний
1 Nov2006

Иногда вам может быть нужно реализовать т.н. контролируемое скачивание, когда все запросы на скачивание файлов передаются скрипту, который решает, как поступить: отправить пользователю какой-либо файл, или показать стриницу access denied, или, может быть, сделать что-то еще. При использовании сервера lighttpd это может быть реализовано при помощи заголовка X-Sendfile, возвращаемого из скрипта. Nginx имеет свою союственную реализацию описанной идеи с использованием заголовка X-Accel-Redirect. В этой короткой статье я попытаюсь описать, как использовать эту возможность из приложений на PHP или Rails.

Представим, что у вас есть какой-либо сайт, работающий на Apache с PHP или Rails для генерации нинамического контента. Если вы будете использовать nginx в качестве reverse-proxy перед вашим сервером Apache, вы получите сразу две положительных возможности:

  1. Вы сможете освободить больше ресурсов вашего сервера для обслуживания клиентов, т.к. nginx возьмет на себя работу с медленными клиентами (детальнее – здесь).
  2. Вы сможете реализовать контролируемое скачивание статических файлов с вашего сайта.

В этой статье я предпологаю, что сайт расположен в каталоге /var/www и статические файлы (например, фильмы, музыка или что-то еще) расположены в каталоге /var/www/files. Apache слушает на порту http://127.0.0.1:8080.

Для начала, давайте рассмотрим нашу конфигурацию сервера nginx:

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;
        }
    }
}

Как вы видите, у нас есть дополнительная “internal” секция location /files. Это ключевое влово “internal” позволяет нам иметь секции location, которые будут доступны для польщователя только в случае внутренних редиректов внутри nginx и при использование заголовка X-Accel-Redirect в ответах от сриптов backend-сервера. Итак, мы можем использовать простой скрипт на PHP или код на Rails для реализации контролируемых скачиваний с поддержкой заголовков Ranges (докачка) и всех остальных возможностей, предоставляемых при прямом скачивании статического контента с серверов под управлением nginx.

Вот пример очень простого скрипта down.php:

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);

?>

В приложениях Rails вы можете использовать следубщий код в вашем 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

Вот и все! При помощи описанного подхода вы сможете реализовать очень гибкую и удивительно эффективную систему раздачи любого статического контента!