Top.Mail.Ru
OSEN-НИЙ SAAALEСкидка 50% на виртуальный хостинг и VDS
до 30.11.2025 Подробнее
Выберите продукт

Большие загрузки и выдача файлов: client_max_body_size, upload_max_filesize, X‑Accel‑Redirect

413 Request Entity Too Large, пустой $_FILES, обрывы по таймаутам и «тяжёлая» раздача через PHP. Разбираем Nginx+PHP: лимиты и таймауты, временные каталоги, X‑Accel‑Redirect и корректный resume через Range, плюс чек‑листы и конфиги.
Большие загрузки и выдача файлов: client_max_body_size, upload_max_filesize, X‑Accel‑Redirect

Большие загрузки и тяжёлая выдача файлов — одни из самых «кинжальных» сценариев для веб-сервера. Здесь всплывают 413 Request Entity Too Large, 499 и 504 таймауты, пустые массивы $_FILES, забитые временные каталоги и «горячие» PHP-процессы, которые зачем-то стримят клипу 8 ГБ пользователю. В этой статье разбираем, как правильно согласовать лимиты Nginx и PHP (client_max_body_size, upload_max_filesize, post_max_size), какие таймауты действительно важны, как организовать безопасную отдачу больших файлов через X-Accel-Redirect и сделать так, чтобы пользователи могли докачивать файлы (resume downloads) через Range-запросы.

Как проходит загрузка: краткая схема Nginx + PHP-FPM

Клиент отправляет POST multipart/form-data на ваш сайт. Nginx принимает тело запроса и складывает его во временные файлы (по частям), затем передаёт запрос в PHP-FPM (через FastCGI). PHP читает данные из файлов/потока и раскладывает их в $_FILES. Если по пути что-то «узкое» — запрос отклоняется или рвётся по таймауту.

Критично: при несогласованных лимитах тело запроса может быть отброшено ещё на уровне Nginx (413) — до попадания в PHP. Тогда $_FILES будет пустым.

Три главных лимита размера

Nginx: client_max_body_size

client_max_body_size ограничивает общий размер тела запроса. Применяется на ранней стадии. Если превышен — Nginx вернёт 413 без обращения к приложению. Удобно, если вы хотите экономить ресурсы и трафик, но пользователь увидит стандартную страницу ошибки, если не настроить error_page.

PHP: upload_max_filesize и post_max_size

upload_max_filesize — максимальный размер одного загружаемого файла. post_max_size — максимальный общий размер тела POST, включая все поля и файлы. Обычно post_max_size должен быть больше или равен upload_max_filesize с некоторым запасом (пары мегабайт хватает).

Типичные симптомы несогласованности:

  • Nginx меньше PHP: пользователь получает 413 мгновенно, приложение не видит запрос.
  • PHP меньше Nginx: загрузка идёт долго, а потом приложение сообщает о превышении лимита; ресурсы тратятся больше, зато можно показать дружелюбную ошибку.

Практика: в проектах, где UX важнее экономии трафика, ставьте лимит Nginx чуть больше лимитов PHP, чтобы ошибка обрабатывалась приложением. В API и сильной нагрузке — наоборот, Nginx жёстче режет заранее.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Не только размер: таймауты и буферы

Даже при правильных лимитах большие загрузки могут упираться в таймауты или дисковые ограничения. Вот что важно проверить:

  • client_body_timeout (Nginx): время ожидания следующего фрагмента тела запроса. Если пользователь грузит медленно — увеличьте.
  • send_timeout (Nginx): таймаут отправки ответа клиенту (включая выдачу файлов).
  • keepalive_timeout (Nginx): полезно, но к загрузке напрямую не относится.
  • fastcgi_read_timeout (Nginx): сколько ждать ответа от PHP. Большой парсинг/обработка — увеличьте осторожно.
  • client_body_buffer_size и tmp-path-и Nginx: куда и как буферизуется тело запроса до передачи в бэкенд.
  • max_execution_time, max_input_time (PHP): ограничивают длительность обработки/чтения входа.
  • memory_limit (PHP): не относится напрямую к файлу, но может «сбросить» скрипт при неосторожном чтении в память.
  • upload_tmp_dir (PHP): временный каталог для загружаемых файлов; проверьте квоты/прав доступа/свободное место.

Если нужно тонко управлять параметрами и I/O под нагрузкой, удобнее делать это на VDS. Если вы обслуживаете проект на шаред-площадке — уточните доступные лимиты у провайдера или используйте наш виртуальный хостинг с актуальными сборками PHP и Nginx.

Поток загрузки в Nginx+PHP и ключевые таймауты/буферы

Где хранится тело запроса: временные каталоги

При больших загрузках тело запроса буферизуется во временные файлы. Две точки отказа — нехватка места и права:

  • Nginx: client_body_temp_path (по умолчанию каталог temp внутри префикса Nginx). Должен лежать на файловой системе с достаточным свободным местом и скоростью.
  • PHP: upload_tmp_dir. Если не задан, используется системный TMP. На хостингах часто ограничен квотой.

Если места мало — загрузки «случайно» обрываются, в логах Nginx/FPM будут ошибки наподобие «No space left on device» или проблемы с перемещением файла (move_uploaded_file возвращает false).

Базовый чек-лист настроек

  1. Определите максимально допустимый размер загружаемых файлов и общий размер формы.
  2. Выберите стратегию отказа (ранний 413 в Nginx или обработка на уровне приложения).
  3. Согласуйте client_max_body_size, post_max_size, upload_max_filesize.
  4. Проверьте client_body_timeout и fastcgi_read_timeout.
  5. Назначьте и проверьте каталоги временных файлов в Nginx и PHP, следите за диском и inode.
  6. Убедитесь, что ulimit и системные лимиты файлов/сокетов достаточны под пиковую нагрузку.

Пример конфигурации Nginx для больших загрузок

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    client_max_body_size 1024m;  # 1 ГБ
    client_body_timeout 120s;
    send_timeout 120s;

    # При необходимости перенесите temp в отдельный быстрый раздел
    # client_body_temp_path /var/lib/nginx/body 1 2;
}

server {
    listen 80;
    server_name example.local;

    # Кастомная страница для 413
    error_page 413 = /413.html;
    location = /413.html { internal; }

    location / {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
        fastcgi_pass unix:/run/php/php-fpm.sock;
        fastcgi_read_timeout 180s;
    }
}

PHP (php.ini) под крупные загрузки

file_uploads = On
upload_max_filesize = 1024M
post_max_size = 1026M
max_file_uploads = 50
max_input_time = 180
max_execution_time = 180
memory_limit = 512M
; upload_tmp_dir = /path/to/php-tmp  ; при необходимости вынесите на отдельный том

Частая ошибка — поставить upload_max_filesize больше post_max_size. В таком случае загрузка «сломается» тихо: файл будто бы игнорируется, а $_FILES пустой. Обязательно проверяйте корректность значений и единиц (M/G vs Mb/Gb).

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Проверка через curl

Полезно проверять лимиты без фронтенда:

# Отправить файл и проверить код ответа
curl -i -X POST \
  -F "file=@./big.bin" \
  http://example.local/upload

# Проверить, поддерживает ли сервер докачку (Range)
curl -I http://example.local/download/big.iso
curl -r 0-1048575 -o part.bin http://example.local/download/big.iso

Почему большие файлы нельзя отдавать через PHP

Технически можно вызвать readfile() и написать цикл с fpassthru(). Но это перегружает PHP-FPM: процесс будет занят десятками секунд или минут, упираясь в send_timeout и сетевую скорость клиента. К тому же PHP хуже поддерживает Range-запросы и докачку «из коробки»; нужно вручную ставить заголовки, следить за буферами, отключать output_buffering, избегать случайного вывода и пр.

Оптимальный путь — переложить выдачу файла на Nginx через внутренний редирект X-Accel-Redirect. Так Nginx сам будет читать файл с диска, эффективно буферизовать и корректно работать с Range-запросами и медленными клиентами, не блокируя PHP.

Схема X‑Accel‑Redirect

  1. Файлы хранятся вне веб-корня или в защищённом каталоге, недоступном напрямую.
  2. PHP выполняет авторизацию/аудит, проверяет право доступа к файлу.
  3. PHP возвращает пользователю обычный ответ, но добавляет заголовок X-Accel-Redirect с внутренним путём, понятным Nginx.
  4. Nginx перехватывает заголовок и отдаёт файл напрямую, применяя свои оптимизации.

Nginx: защищённое внутреннее хранилище

server {
    listen 80;
    server_name example.local;

    # Внутренний location для X-Accel-Redirect
    location /protected/ {
        internal;
        alias /data/protected/;  # файлы лежат вне веб-корня
        # Опционально ограничить скорость
        # limit_rate_after 5m;
        # limit_rate 1m;
    }

    location /download {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/download.php;
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }
}

PHP: заголовки и редирект для Nginx

<?php
// download.php

// 1) Авторизация и проверка доступа к файлу
$userId = 123; // пример
$filePath = "/data/protected/reports/report-{$userId}.pdf";
if (!is_file($filePath)) {
    http_response_code(404);
    exit('Not found');
}

$publicName = 'report.pdf';
$internalUri = '/protected/' . basename($filePath);
$filesize = filesize($filePath);

// 2) Заголовки ответа (важно: Content-Type/Disposition/Length)
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . $publicName . '"');
header('Content-Length: ' . $filesize);
header('Accept-Ranges: bytes'); // Nginx всё равно поддержит, но явно — понятнее

// 3) Главный трюк: поручаем отдачу Nginx
header('X-Accel-Redirect: ' . $internalUri);
exit;

Обратите внимание: location /protected/ помечен как internal, значит прямой доступ к нему запрещён. Только приложение может «включать» выдачу через заголовок X-Accel-Redirect.

Отдача защищённых файлов через X‑Accel‑Redirect в Nginx

Поддержка Range и resume downloads

При отдаче файлов Nginx умеет обрабатывать заголовок Range и корректно отвечать частями (206 Partial Content) со всеми нужными заголовками (Accept-Ranges, Content-Range). Благодаря этому докачка в большинстве браузеров и менеджеров загрузок работает «из коробки». Если файл отдаётся через X-Accel-Redirect, поддержку Range обеспечивает сам Nginx, а не PHP.

Для больших файлов полезно включить низкоуровневые оптимизации чтения:

  • sendfile on; — отдача напрямую из файлового кэша ядра.
  • tcp_nopush on; и tcp_nodelay on; — аккуратная отправка больших блоков.
  • aio on; — асинхронное чтение (актуально для некоторых ОС и паттернов I/O).

Если нужно сбалансировать сжатие, кеш и производительность PHP на шаред-площадке — посмотрите также разбор оптимизаций в материале про PHP, OPCache и Brotli.

Тонкости буферизации и заголовков

  • Content-Length: лучше указывать точный размер, чтобы клиенты корректно показывали прогресс и умели докачивать. При X-Accel-Redirect Nginx может сам вычислить длину файла, но явный Content-Length в ответе от PHP не навредит.
  • Content-Type: ставьте корректный MIME; не полагайтесь на дефолтный application/octet-stream, если тип известен.
  • Content-Disposition: attachment; filename="..." — для «скачивания как»; аккуратнее с не-ASCII именами (используйте filename* при необходимости).
  • Кеширование: если файлы статичны, используйте expires/cache-control в internal-локации. Для приватного контента ставьте Cache-Control: private.

Типовые ошибки и их диагностика

413 Request Entity Too Large

Причины: client_max_body_size меньше фактического размера. Лечится увеличением лимита и/или настройкой кастомной страницы ошибки. Если хотите, чтобы обработка была в приложении, увеличьте лимит Nginx выше лимитов PHP.

Пустой $_FILES

Чаще всего post_max_size меньше, чем отправленный размер, или закончился диск в upload_tmp_dir. Проверьте php.ini, права на TMP и логи PHP-FPM. Не забывайте про max_file_uploads при множественных инпутах.

499/504, «загрузка останавливается»

499 — клиент сам закрыл соединение (часто из-за низкой скорости или потери сети). 504 Gateway Timeout — истёк fastcgi_read_timeout при обработке сервером. Увеличьте таймауты, проверьте диск, I/O и сетевые условия. При загрузке с очень медленных линий поднимите client_body_timeout.

Выдача больших файлов «через PHP» под завязку грузит CPU

Переведите выдачу на X-Accel-Redirect. Контролируйте скорость и используйте sendfile. Это резко разгружает PHP-FPM и позволяет обслуживать больше одновременных скачиваний.

Недокачка и «битые» файлы

Проверьте, не перезаписывает ли приложение заголовки, нет ли лишнего вывода до заголовков (BOM, пробелы), и что по пути нет обратного прокси, который режет Range. Для больших файлов лучше исключить gzip на лету.

Логи для анализа больших запросов

Для наблюдения за проблемами полезно логировать длину запроса и потраченное время:

log_format upload '$remote_addr - $remote_user [$time_local] '
                  '"$request" $status $body_bytes_sent '
                  '$request_length $request_time';
access_log /var/log/nginx/upload.log upload;

$request_length покажет, насколько большой был запрос, а $request_time поможет понять, где узкое место (сеть, диск, приложение).

Рекомендации по проектированию

  • Сохраняйте загружаемые файлы вне веб-корня и публикуйте их только через X-Accel-Redirect с авторизацией.
  • Ограничивайте максимально допустимые размеры по ролям/эндпоинтам (админка vs публичная форма), не размывайте единственный глобальный лимит.
  • Следите за временными каталогами и дисками (метрики, алерты). «Внезапная» нехватка места — частая причина падений загрузок.
  • Если фронтенд поддерживает чанковую загрузку, используйте серверную сборку частей — она стабильнее на нестабильных сетях. На сервере всё равно проверьте итоговый размер.
  • Для файлов >2–4 ГБ (особенно на сетевых FS) уделите внимание aio и RAID/блочному уровню — от этого заметно зависит стабильность выдачи.

Если планируете самостоятельную администрируемую инфраструктуру, посмотрите обзор панелей для управления своим сервером: сравнение панелей для VDS.

Итоги

Чтобы большие загрузки работали стабильно, согласуйте лимиты client_max_body_size, upload_max_filesize и post_max_size, не забудьте про таймауты и временные каталоги, а для выдачи переходите на X-Accel-Redirect. Так вы обеспечите предсказуемость, снимете нагрузку с PHP-FPM, получите корректную поддержку Range/ресюма и убережёте пользователей от «битых» скачиваний. Не бойтесь жёстко резать слишком большие запросы на уровне Nginx там, где это оправдано, и давайте приложению контролировать UX там, где это важнее.

Поделиться статьей

Вам будет интересно

IPv6 в проде: AAAA-записи, конфиг веб‑сервера и файрвол на VDS OpenAI Статья написана AI Fastfox

IPv6 в проде: AAAA-записи, конфиг веб‑сервера и файрвол на VDS

Готовим прод к IPv6: адрес на VDS, AAAA в DNS, nginx/Apache на [::]:80/443, безопасный файрволл (nftables/ufw), ICMPv6 и sysctl. В ...
Перенос домена к другому регистратору без простоев: EPP‑код, transfer lock и DNS OpenAI Статья написана AI Fastfox

Перенос домена к другому регистратору без простоев: EPP‑код, transfer lock и DNS

Разбираем перенос домена без простоя: где взять EPP‑код, как снять transfer lock, какие письма придут от регистратора и что делать ...
Горячие бэкапы на VDS: LVM snapshots для файлов и баз данных OpenAI Статья написана AI Fastfox

Горячие бэкапы на VDS: LVM snapshots для файлов и баз данных

Хотите снимать горячие бэкапы на VDS без простоя? Разбираем LVM snapshots для файловых систем и MySQL: как подготовить VG/LV, безо ...