Nginx + PHP-FPM: как работает связка и решаем ошибки 502 и 504
Nginx + PHP-FPM: как работает связка и почему возникают ошибки 502 и 504
> Часть 3 серии про Nginx
> В прошлой части мы разобрали virtual hosts и структуру сайтов.
> Теперь переходим к главному: как Nginx выполняет PHP-код и что такое PHP-FPM на самом деле.
---
Введение
Nginx + PHP-FPM — это стандартная связка для обработки PHP-приложений в продакшене. Понимание принципов её работы критически важно для диагностики и решения проблем.
В этой статье разберём:
- архитектуру взаимодействия Nginx и PHP-FPM;
- типичные ошибки 502 и 504 и их причины;
- практические методы диагностики;
- настройку таймаутов и пулов процессов;
- полные примеры конфигураций для реальных проектов.
---
Главная мысль, которую нужно понять сразу
👉 Nginx НЕ исполняет PHP. Вообще. Никогда.
Nginx — это веб-сервер, который:
- принимает HTTP-запросы;
- анализирует URL и заголовки;
- передаёт PHP-файлы в PHP-FPM через протокол FastCGI;
- ждёт результат выполнения;
- отдаёт ответ клиенту.
Если PHP-FPM не отвечает или работает некорректно — проблема не в Nginx, а в конфигурации или состоянии PHP-FPM.
---
Что такое PHP-FPM простыми словами
PHP-FPM (FastCGI Process Manager) — это менеджер процессов PHP, который:
- запускает и управляет PHP-процессами;
- поддерживает пул воркеров для параллельной обработки запросов;
- принимает запросы от Nginx через протокол FastCGI;
- выполняет PHP-код из .php файлов;
- возвращает результат выполнения (HTML, JSON и т.д.) обратно в Nginx.
Схема взаимодействия выглядит так:
```text
Браузер
↓ HTTP-запрос
Nginx
↓ FastCGI-протокол
PHP-FPM
↓ выполнение кода
PHP-процесс
↓ результат
PHP-FPM → Nginx → Браузер
```
---
Как Nginx определяет, что файл — PHP
Nginx использует директиву location с регулярным выражением для определения PHP-файлов:
```nginx
location ~ \.php$ {
конфигурация обработки PHP
}
```
Это означает:
- любой запрос, URL которого заканчивается на .php;
- будет обработан этим блоком location;
- и передан в PHP-FPM для выполнения.
---
Базовая конфигурация Nginx для PHP
Минимально рабочая конфигурация для обработки PHP:
```nginx
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
```
Разберём каждую директиву:
- include fastcgi_params; — подключает стандартные параметры FastCGI;
- fastcgi_pass — указывает, куда передавать запрос (сокет или TCP);
- fastcgi_index — индексный файл по умолчанию;
- fastcgi_param SCRIPT_FILENAME — самая важная строка, указывает PHP-FPM путь к файлу для выполнения.
---
fastcgi_pass — способы подключения к PHP-FPM
Есть два варианта подключения Nginx к PHP-FPM.
Вариант 1. Unix-сокет (рекомендуется)
```nginx
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
```
Преимущества:
- быстрее (нет сетевых задержек);
- безопаснее (нет сетевого доступа);
- стандартный способ для одного сервера;
- меньше накладных расходов.
Недостатки:
- работает только локально на том же сервере.
Вариант 2. TCP-подключение
```nginx
fastcgi_pass 127.0.0.1:9000;
```
Используется, если:
- PHP-FPM запущен в Docker-контейнере;
- PHP-FPM находится на другом сервере;
- нужна сетевая изоляция между процессами.
---
Где узнать путь к сокету PHP-FPM
Путь к сокету указывается в конфигурационном файле пула PHP-FPM.
Ubuntu / Debian
```bash
/etc/php/8.2/fpm/pool.d/www.conf
```
Ищите строку:
```ini
listen = /run/php/php8.2-fpm.sock
```
CentOS / Rocky Linux
```bash
/etc/php-fpm.d/www.conf
```
👉 Этот путь должен точно совпадать с fastcgi_pass в конфигурации Nginx.
---
SCRIPT_FILENAME — самая важная строка
```nginx
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
```
Если эта строка неправильная или отсутствует → 502 Bad Gateway гарантирован.
Что здесь происходит:
- $document_root — значение директивы root из блока server;
- $fastcgi_script_name — путь к PHP-файлу из URL запроса;
- результат — полный абсолютный путь к файлу на диске.
Пример:
```text
URL запроса: /index.php
root: /var/www/site/public
Итоговый SCRIPT_FILENAME:
/var/www/site/public/index.php
```
PHP-FPM использует этот путь для загрузки и выполнения файла.
---
index.php и try_files — правильная связка
Для современных PHP-фреймворков (Laravel, Symfony, Bitrix, WordPress) критически важно правильно настроить try_files:
```nginx
location / {
try_files $uri $uri/ /index.php?$query_string;
}
```
Что это даёт:
- если файл существует — отдаём его напрямую;
- если это директория — пробуем найти индексный файл;
- если ничего не найдено — передаём запрос в index.php с сохранением query string;
- идеально для фреймворков с роутингом через index.php.
---
Полный пример server block для PHP-проекта
Продакшен-адекватная конфигурация для PHP-приложения:
```nginx
server {
listen 80;
server_name site.ru www.site.ru;
root /var/www/site.ru/public;
index index.php index.html;
access_log /var/www/site.ru/logs/access.log;
error_log /var/www/site.ru/logs/error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
Таймауты (подробнее ниже)
fastcgi_read_timeout 60s;
fastcgi_connect_timeout 60s;
}
Защита скрытых файлов
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
```
👉 Это здоровый, продакшен-адекватный минимум для большинства PHP-проектов.
---
Готовые конфигурации для популярных фреймворков
Для конкретных фреймворков и CMS могут потребоваться дополнительные настройки. Ниже приведены ссылки на готовые конфигурации, основанные на официальной документации:
- Nginx для Bitrix CMS — конфигурация с поддержкой URL rewriting через /bitrix/urlrewrite.php, защитой директорий и увеличенными таймаутами для операций Bitrix.
- Nginx для WordPress — конфигурация с поддержкой pretty permalinks, защитой директории uploads и оптимизацией для WordPress.
- Nginx для Laravel — конфигурация с правильной настройкой root на директорию public, обработкой только index.php и поддержкой Laravel роутинга.
- Nginx для Astro (статический сайт) — конфигурация для статических Astro сайтов с поддержкой SPA роутинга, кеширования и gzip сжатия.
Все конфигурации проверены и основаны на официальной документации соответствующих проектов.
---
Ошибка 502 Bad Gateway — причины и диагностика
Что означает 502
Nginx не смог получить ответ от PHP-FPM или получил некорректный ответ.
Частые причины
- PHP-FPM не запущен — самая частая причина;
- Неверный путь к сокету — сокет не существует или путь указан неправильно;
- PHP упал с fatal error — критическая ошибка в PHP-коде;
- Недостаточно памяти — сервер исчерпал доступную память;
- Неверный SCRIPT_FILENAME — PHP-FPM не может найти файл для выполнения;
- Проблемы с правами доступа — Nginx не может прочитать сокет или файлы.
Диагностика по шагам
Шаг 1. Проверка статуса PHP-FPM:
```bash
systemctl status php8.2-fpm
```
Если сервис не запущен:
```bash
systemctl start php8.2-fpm
systemctl enable php8.2-fpm
```
Шаг 2. Проверка существования сокета:
```bash
ls -l /run/php/
```
Должен быть файл php8.2-fpm.sock (или другой, в зависимости от версии).
Шаг 3. Проверка логов PHP-FPM:
```bash
tail -f /var/log/php8.2-fpm.log
```
Или для конкретного пула:
```bash
tail -f /var/log/php8.2-fpm/www-error.log
```
Шаг 4. Проверка логов Nginx:
```bash
tail -f /var/www/site.ru/logs/error.log
```
Или системный лог:
```bash
tail -f /var/log/nginx/error.log
```
Шаг 5. Проверка конфигурации:
```bash
nginx -t
php-fpm8.2 -t
```
👉 Начинайте всегда с логов, не с конфигов. Логи покажут реальную причину проблемы.
---
Ошибка 504 Gateway Timeout — причины и решения
Что означает 504
PHP слишком долго выполняет запрос, Nginx устал ждать и закрыл соединение.
Типовые причины
- Тяжёлые запросы к базе данных — медленные SQL-запросы без индексов;
- Внешние API без таймаутов — зависание на запросах к внешним сервисам;
- Зависший PHP-процесс — бесконечный цикл или блокирующая операция;
- Маленькие лимиты пула FPM — все процессы заняты, новые запросы ждут;
- Неправильные настройки таймаутов — несоответствие между Nginx, PHP-FPM и PHP.
---
Настройка таймаутов на всех уровнях
Таймауты нужно настраивать на трёх уровнях, и они должны быть согласованы.
В Nginx
```nginx
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
Время ожидания ответа от PHP-FPM
fastcgi_read_timeout 60s;
Время на установку соединения с PHP-FPM
fastcgi_connect_timeout 60s;
Время на отправку запроса в PHP-FPM
fastcgi_send_timeout 60s;
}
```
В PHP-FPM (pool.d/www.conf)
```ini
; Максимальное время выполнения запроса (в секундах)
; 0 = без ограничений (не рекомендуется)
request_terminate_timeout = 60
```
В PHP (php.ini)
```ini
; Максимальное время выполнения скрипта (в секундах)
max_execution_time = 60
; Максимальное время загрузки файла
max_input_time = 60
```
👉 Все три уровня должны быть согласованы. Рекомендуется устанавливать одинаковые значения или делать таймаут PHP-FPM немного больше, чем PHP.
Пример согласованной настройки:
```text
Nginx fastcgi_read_timeout: 60s
PHP-FPM request_terminate_timeout: 65s
PHP max_execution_time: 60s
```
Это даёт небольшой запас для завершения процесса после таймаута PHP.
---
Настройка пула PHP-FPM
Каждый pool — это отдельный набор PHP-процессов с собственными настройками. Правильная настройка пула критически важна для производительности.
Базовые параметры пула
```ini
; Режим управления процессами: static, dynamic, ondemand
pm = dynamic
; Максимальное количество дочерних процессов
pm.max_children = 50
; Количество процессов, запускаемых при старте
pm.start_servers = 10
; Минимальное количество свободных процессов
pm.min_spare_servers = 5
; Максимальное количество свободных процессов
pm.max_spare_servers = 15
; Количество запросов, после которого процесс перезапускается
pm.max_requests = 500
```
Что это значит
- pm.max_children — максимум одновременных запросов. Если все процессы заняты → новые запросы ждут → возможен 504;
- pm.start_servers — сколько процессов запускается при старте PHP-FPM;
- pm.min_spare_servers — минимальное количество процессов в режиме ожидания;
- pm.max_spare_servers — максимальное количество процессов в режиме ожидания;
- pm.max_requests — после этого количества запросов процесс перезапускается (защита от утечек памяти).
Расчёт оптимального количества процессов
Формула для pm.max_children:
```text
max_children = (RAM доступная для PHP) / (RAM на один PHP-процесс)
```
Пример:
```text
Доступно RAM: 2 GB
Один PHP-процесс: ~50 MB
max_children = 2048 MB / 50 MB ≈ 40 процессов
```
Режимы управления процессами
pm = static — фиксированное количество процессов:
```ini
pm = static
pm.max_children = 20
```
Подходит для стабильной нагрузки, предсказуемое потребление памяти.
pm = dynamic — динамическое управление (рекомендуется):
```ini
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
```
Подходит для переменной нагрузки, оптимальное использование ресурсов.
pm = ondemand — процессы запускаются по требованию:
```ini
pm = ondemand
pm.max_children = 50
pm.process_idle_timeout = 10s
```
Подходит для серверов с низкой нагрузкой, экономия ресурсов.
---
Типовые ошибки новичков
❌ Путают Apache mod_php и PHP-FPM — думают, что PHP встроен в Nginx, как в Apache.
❌ Думают, что PHP — часть Nginx — не понимают архитектуру FastCGI.
❌ Правят php.ini, а проблема в FPM — не различают настройки PHP и PHP-FPM.
❌ Игнорируют логи — начинают менять конфиги наугад вместо чтения логов.
❌ Сразу крутят таймауты — увеличивают таймауты, не понимая реальную причину медленной работы.
❌ Неправильный порядок location — блок location ~ \.php$ должен быть после location /.
❌ Забывают про try_files — без него фреймворки с роутингом не работают.
---
Чек-лист диагностики проблем с PHP
Если PHP не работает, проверьте по порядку:
- [ ] PHP-FPM запущен — systemctl status php8.2-fpm
- [ ] Путь к сокету совпадает — проверьте fastcgi_pass в Nginx и listen в pool.conf
- [ ] SCRIPT_FILENAME корректный — должен содержать полный путь к файлу
- [ ] try_files настроен — для фреймворков обязателен
- [ ] Логи читаются — начните с tail -f на error.log
- [ ] Нет fatal error — проверьте логи PHP-FPM
- [ ] Права доступа корректны — Nginx должен читать файлы и сокет
- [ ] Конфигурация синтаксически правильна — nginx -t и php-fpm8.2 -t
- [ ] Достаточно памяти — проверьте free -h и логи OOM killer
- [ ] Таймауты согласованы — проверьте на всех трёх уровнях
---
Рекомендации по настройке и безопасности
Производительность
- Используйте Unix-сокеты вместо TCP для локальных подключений.
- Настройте кэширование статических файлов в Nginx.
- Используйте OPcache для PHP (включён по умолчанию в современных версиях).
- Мониторьте использование памяти и CPU процессами PHP-FPM.
Безопасность
- Ограничьте выполнение PHP только в нужных директориях.
- Используйте отдельные пулы для разных сайтов с разными правами.
- Настройте open_basedir в PHP для ограничения доступа к файлам.
- Регулярно обновляйте PHP и PHP-FPM до актуальных версий.
Мониторинг
- Настройте ротацию логов для предотвращения переполнения диска.
- Используйте мониторинг процессов PHP-FPM (например, через systemd или внешние системы).
- Отслеживайте метрики: количество активных процессов, время выполнения запросов, ошибки.
---
Что будет в части 4
Дальше логичное продолжение:
Часть 4 — Reverse Proxy и проксирование (API, Node.js, Python)
- proxy_pass и проксирование запросов;
- работа с заголовками;
- WebSocket через Nginx;
- настройка backend-сервисов за Nginx.
---
Заключение
Связка Nginx + PHP-FPM — это не магия, а чёткий контракт между компонентами:
- Nginx — маршрутизатор и точка входа, принимает HTTP-запросы;
- PHP-FPM — исполнитель PHP-кода, управляет процессами;
- FastCGI — протокол взаимодействия между ними;
- Ошибки почти всегда читаются в логах — начинайте диагностику с них.
Когда вы понимаете эту архитектуру, 502 и 504 перестают пугать — они становятся понятными сигналами о конкретных проблемах, которые можно быстро диагностировать и исправить.
Правильная настройка таймаутов, пулов процессов и мониторинг логов — это основа стабильной работы PHP-приложений в продакшене.