Связка веб-сервера Nginx и менеджера процессов PHP-FPM (FastCGI Process Manager) — стандартный стек для веб-приложений на базе PHP. В этой архитектуре Nginx принимает входящие сетевые соединения, обрабатывает TLS/SSL, отдает статический контент и проксирует динамические запросы. PHP-FPM отвечает за интерпретацию и исполнение программного кода в изолированных дочерних процессах (воркерах).
Нарушения в сетевом взаимодействии между сервисами, некорректные права доступа, ошибки в конфигурации пулов процессов или несогласованные таймауты приводят к сбоям в обслуживании пользователей. Чаще всего они выражаются в виде HTTP-ответов 502 Bad Gateway и 504 Gateway Time-out.
Как работает связка Nginx и PHP-FPM: жизненный цикл запроса
Nginx не имеет встроенного интерпретатора PHP. Вместо прямого исполнения скрипта веб-сервер работает в режиме обратного прокси (reverse proxy) и транслирует запросы через бинарный протокол FastCGI.
Цепочка обработки запроса строится следующим образом:
- Прием запроса: Клиент отправляет HTTP-запрос к Nginx.
- Маршрутизация: Nginx сопоставляет URI с блоками
serverиlocation. Если запрос соответствует правилу обработки PHP (например,location ~ \.php$), веб-сервер инициирует FastCGI-сессию. - Формирование параметров: Nginx преобразует HTTP-заголовки и метаданные в переменные протокола FastCGI (
SCRIPT_FILENAME,REQUEST_METHOD,QUERY_STRINGи др.). - Передача в сокет: Данные направляются по транспорту, указанному в директиве
fastcgi_pass(Unix Domain Socket или TCP-порт). - Обработка в пуле: Главный процесс PHP-FPM (master process) распределяет задачу свободному дочернему процессу (worker). Воркер выполняет PHP-скрипт, обращается к базе данных или сторонним API и генерирует тело ответа.
- Возврат данных: PHP-FPM передает статус, HTTP-заголовки и тело ответа обратно веб-серверу.
- Ответ клиенту: Nginx отправляет сформированный HTTP-ответ пользователю.
[Клиент]
│ HTTP / HTTPS
▼
[Nginx]
│ FastCGI (fastcgi_pass)
│ ├── Unix Domain Socket (/run/php/php-fpm.sock)
│ └── TCP Socket (127.0.0.1:9000)
▼
[PHP-FPM Master Process]
│
├── [Worker 1] -> Выполнение скрипта (index.php)
├── [Worker 2] -> Свободен
└── [Worker N] -> Свободен
Роль FastCGI и передача параметров
Протокол FastCGI поддерживает постоянный пул процессов для обработки запросов, исключая накладные расходы на запуск нового интерпретатора при каждом обращении.
Чтобы PHP-FPM корректно определил файл для исполнения на диске, Nginx передает системные переменные. Критически важной является директива fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;. Без нее менеджер процессов не сможет найти целевой файл и вернет ошибку Primary script unknown с кодом 404 или статус 502.
Unix Domain Socket против TCP Socket: что и когда выбирать
Для обмена данными между Nginx и PHP-FPM применяются два типа сетевых транспортов:
- Unix Domain Socket (UDS): Специальный файл сокета в файловой системе (например,
unix:/run/php/php8.2-fpm.sock).- Преимущества: Работает напрямую через оперативную память и ядро Linux без сетевого стека, обеспечивает минимальные задержки (latency) и не расходует TCP-порты.
- Ограничения: Доступен только в пределах одной операционной системы (одного хоста или общего volume в контейнерах). Требует корректной настройки прав доступа на уровне файлов.
- TCP Socket: Сетевое соединение по протоколу TCP/IP (например,
127.0.0.1:9000или удаленный сетевой адрес).- Преимущества: Позволяет разносить веб-сервер и обработчики PHP по разным серверам или виртуальным машинам.
- Ограничения: Создает дополнительные накладные расходы на упаковку сетевых пакетов и при высокой нагрузке может исчерпать доступные локальные порты (
TIME_WAIT).
Рекомендация: Если Nginx и PHP-FPM находятся на одном хосте, предпочтительно использовать Unix Domain Socket. Если веб-сервер распределяет нагрузку на отдельный кластер серверов приложений — используется TCP Socket.
Базовая настройка Nginx и пула PHP-FPM
Для стабильной работы связки параметры виртуального хоста Nginx должны быть согласованы с конфигурацией пула PHP-FPM.
Конфигурация Nginx: блок location ~ \.php$
Пример настройки виртуального хоста Nginx с передачей запросов через Unix-сокет:
server {
listen 80;
server_name example.test;
root /var/www/app/public;
index index.php index.html;
access_log /var/log/nginx/app_access.log;
error_log /var/log/nginx/app_error.log warn;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
# Защита от исполнения несуществующих файлов
try_files $uri =404;
# Базовые параметры FastCGI
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Точка подключения к PHP-FPM
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
# Буферизация и таймауты
fastcgi_connect_timeout 5s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
}
location ~ /\.ht {
deny all;
}
}
Конфигурация пула PHP-FPM: директивы listen, pm и права доступа
Конфигурация пула по умолчанию располагается в /etc/php/8.X/fpm/pool.d/www.conf (Debian/Ubuntu) или /etc/php-fpm.d/www.conf (RHEL/AlmaLinux/Rocky).
Пример пула с Unix-сокетом:
[www]
user = www-data
group = www-data
; Сетевой транспорт
listen = /run/php/php8.2-fpm.sock
; Права доступа к сокету для Nginx
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
; Режим управления процессами
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 1000
; Таймауты и логирование
request_terminate_timeout = 60s
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/www-slow.log
pm.status_path = /fpm-status
При использовании TCP-сокета директива listen настраивается следующим образом:
listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1
Управление процессами и таймаутами: предотвращение деградации
PHP-FPM поддерживает три режима управления процессами (pm):
static: Количество воркеров постоянно и фиксируется параметромpm.max_children. Обеспечивает максимальное быстродействие на выделенных серверах, так как система не тратит ресурсы на создание и завершение процессов.dynamic: Количество процессов изменяется в диапазоне отpm.min_spare_serversдоpm.max_spare_serversв зависимости от нагрузки, не превышаяpm.max_children. Подходит для большинства типовых проектов.ondemand: Процессы создаются только по мере поступления входящих запросов и завершаются при простое. Экономит оперативную память при нерегулярном трафике, но увеличивает время ответа при первом обращении.
Расчет и настройка pm.max_children
Параметр pm.max_children определяет максимальное количество одновременно работающих процессов PHP. Неверный расчет этого значения приводит к нехватке памяти и аварийной остановке служб механизмом ядра Linux Out-of-Memory (OOM) Killer.
Формула безопасного расчета pm.max_children:
max_children = (Доступная RAM для PHP) / (Средний объем памяти одного воркера)
Порядок расчета:
- Вычтите из общего объема RAM сервера память, зарезервированную под системные службы, Nginx, СУБД и кэш.
- Определите среднее потребление памяти одним процессом PHP под реальной нагрузкой:
ps --no-headers -o rss -C php-fpm8.2 | awk '{sum+=$1; count++} END {if (count>0) print sum/count/1024 " MB"}' - Разделите доступную память на средний размер процесса. Например, при доступных 4096 МБ RAM и среднем размере воркера 80 МБ:
4096 / 80 ≈51
Рекомендуемое значение:
pm.max_children = 50.
Директива pm.max_requests (например, 500–1000) перезапускает воркер после обработки указанного количества запросов, устраняя последствия возможных утечек памяти в сторонних библиотеках.
Согласование таймаутов Nginx и PHP
Несогласованность лимитов времени приводит к разрывам соединений. Настройки должны выстраиваться по возрастающей цепочке:
База данных / внешние API (cURL timeout: 10s)
└─► PHP max_execution_time (30s)
└─► PHP-FPM request_terminate_timeout (35s)
└─► Nginx fastcgi_read_timeout (40s)
max_execution_time(вphp.ini): Ограничивает время исполнения скрипта движком PHP (по умолчанию учитывает только процессорное время).request_terminate_timeout(вwww.conf): Принудительно завершает воркер сигналомSIGKILL, если время выполнения превысило лимит. Должно быть немного вышеmax_execution_time.fastcgi_read_timeout(вnginx.conf): Время ожидания ответа от FastCGI-сервера со стороны Nginx. Должно превышатьrequest_terminate_timeout, чтобы PHP успел корректно зафиксировать ошибку до закрытия соединения прокси-сервером.
Диагностика и устранение ошибки 502 Bad Gateway
Код 502 Bad Gateway указывает на то, что Nginx не смог установить связь с PHP-FPM, сокет оказался недоступен или соединение было аварийно прервано со стороны upstream.
[Клиент] ─── HTTP GET ───► [Nginx] ─── FastCGI ───X (Связь оборвана / отказ) [PHP-FPM]
▲ │
└─────── HTTP 502 ──────────────┘
Причина 1: Служба PHP-FPM остановлена или сокет недоступен
Если процесс PHP-FPM не запущен или путь к сокету указан с ошибкой, Nginx фиксирует отказ в соединении.
Запись в /var/log/nginx/error.log:
[error] 1234#0: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 192.0.2.1, server: example.test, request: "GET / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000"
или для Unix-сокета:
[error] 1234#0: *1 connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory) while connecting to upstream...
Решение:
- Проверьте статус службы:
systemctl status php8.2-fpm - Проверьте синтаксис конфигурации и запустите службу:
php-fpm8.2 -t systemctl start php8.2-fpm - Сверьте значение
fastcgi_passв Nginx и директивуlistenвwww.conf.
Причина 2: Неверные права доступа к Unix-сокету
Файл сокета существует, но пользователь рабочего процесса Nginx не имеет прав на чтение и запись.
Запись в /var/log/nginx/error.log:
[error] 1234#0: *1 connect() to unix:/run/php/php8.2-fpm.sock failed (13: Permission denied) while connecting to upstream...
Решение:
- Проверьте владельца и права на файл сокета:
ls -la /run/php/php8.2-fpm.sock - В файле
www.confзадайте корректные параметры для пользователя веб-сервера:listen.owner = www-data listen.group = www-data listen.mode = 0660 - Перезапустите службу PHP-FPM:
systemctl restart php8.2-fpm
Причина 3: Аварийное падение процесса PHP
Если процесс воркера падает до отправки HTTP-заголовков, Nginx фиксирует сброс соединения.
Запись в /var/log/nginx/error.log:
[error] 1234#0: *1 recv() failed (104: Connection reset by peer) while reading response header from upstream...
Возможные факторы:
- Превышение
memory_limit: Скрипт исчерпал лимит памяти. В логе PHP-FPM появляется записьPHP Fatal error: Allowed memory size of X bytes exhausted. - Ошибка сегментации (Segfault): Сбой в расширении PHP. В логе PHP-FPM фиксируется:
WARNING: [pool www] child XXX exited on signal 11 (SIGSEGV). - Вмешательство OOM Killer: Ядро Linux принудительно завершило процесс из-за дефицита памяти. Проверяется командой:
dmesg -T | grep -i oom
Диагностика и устранение ошибки 504 Gateway Time-out
Код 504 Gateway Time-out означает, что Nginx успешно установил соединение, но не получил ответ от PHP-FPM в течение интервала fastcgi_read_timeout.
[Клиент] ─── HTTP GET ───► [Nginx] ─── FastCGI ───► [PHP-FPM Worker] (Долгий запрос)
│ │
│ (Истек fastcgi_read_timeout)│ (Все еще выполняется...)
▲ ▼ ▼
└─────── HTTP 504 ────────┘
Причина 1: Длительные операции и рассинхронизация таймаутов
Задержка вызвана тяжелыми SQL-запросами, обработкой больших файлов или зависанием синхронных вызовов к внешним API.
Запись в /var/log/nginx/error.log:
[error] 1234#0: *1 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 192.0.2.1, server: example.test, request: "POST /export HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:"
Решение:
- Оптимизируйте узкие места в приложении или переведите тяжелые задачи на асинхронные очереди (Redis, RabbitMQ).
- Для штатных длительных операций увеличьте лимиты:
- В Nginx:
fastcgi_read_timeout 300s; - В PHP-FPM (
www.conf):request_terminate_timeout = 300s - В
php.ini:max_execution_time = 300
- В Nginx:
Причина 2: Исчерпание свободных воркеров пула
Если все дочерние процессы заняты, входящие запросы становятся в очередь listen.backlog. Если процесс не освобождается вовремя, Nginx закрывает соединение по таймауту.
Запись в логе PHP-FPM (/var/log/php8.2-fpm.log):
WARNING: [pool www] server reached pm.max_children setting (20), consider raising it
Решение:
- Проверьте потребление CPU и памяти.
- При наличии свободных аппаратных ресурсов увеличьте
pm.max_childrenвwww.conf. - Локализуйте медленные запросы, удерживающие воркеры.
Настройка и анализ slowlog
Встроенный журнал медленных запросов PHP-FPM позволяет зафиксировать стек вызовов функций (stack trace) для долгих операций.
Для включения slowlog в www.conf:
; Порог логирования запросов
request_slowlog_timeout = 3s
; Путь к файлу лога
slowlog = /var/log/php-fpm/www-slow.log
; Глубина трейса
request_slowlog_trace_depth = 20
Пример записи в /var/log/php-fpm/www-slow.log:
[01-Feb-2025 14:22:10] [pool www] pid 4512
script_filename = /var/www/app/public/index.php
[0x00007f8b2c414230] curl_exec() /var/www/app/src/Service/ExternalApi.php:45
[0x00007f8b2c414110] sendRequest() /var/www/app/src/Controller/ApiController.php:28
[0x00007f8b2c414030] handle() /var/www/app/public/index.php:12
В данном примере задержка вызвана выполнением функции curl_exec(). Решением является установка жесткого таймаута на клиентский сетевой запрос (CURLOPT_TIMEOUT).
Чек-лист системных команд для отладки
Для быстрой локализации неполадок используйте следующий набор команд:
# 1. Валидация конфигураций перед перезагрузкой
nginx -t
php-fpm8.2 -t
# 2. Просмотр системных логов в реальном времени
tail -f /var/log/nginx/error.log
tail -f /var/log/php8.2-fpm.log
# 3. Проверка сокетов и очередей соединений
ss -tlpn | grep -E 'php|nginx|9000'
ss -xlpn | grep php
# 4. Проверка прав доступа к файлу сокета
ls -la /run/php/
# 5. Проверка срабатывания OOM Killer ядра
dmesg -T | grep -E -i 'oom|killed process|php-fpm'
# 6. Подсчет числа запущенных воркеров
ps aux | grep php-fpm | grep -v master | wc -l
Часто задаваемые вопросы (FAQ)
1. В чем принципиальная разница между ошибками 502 и 504 в контексте PHP-FPM?
Ошибка 502 Bad Gateway свидетельствует о невозможности подключиться к upstream, сбросе соединения или аварийной остановке процесса воркера. Ошибка 504 Gateway Time-out возникает, когда соединение установлено успешно, но PHP-FPM не сформировал ответ за отведенный интервал fastcgi_read_timeout.
2. Почему Unix-сокет предпочтительнее TCP при размещении на одном сервере?
Unix Domain Socket передает данные в оперативной памяти через механизмы ядра Linux в обход сетевого стека, не требует вычисления контрольных сумм TCP и исключает проблемы с нехваткой свободных локальных портов.
3. Что произойдет, если fastcgi_read_timeout в Nginx меньше max_execution_time в PHP?
Nginx прервет соединение и вернет клиенту ошибку 504. При этом процесс PHP-FPM продолжит исполнение скрипта в фоновом режиме до исчерпания своего лимита времени, нерационально расходуя процессорные ресурсы и память.
4. Как проверить, был ли процесс PHP-FPM остановлен механизмом OOM Killer?
Выполните команду dmesg -T | grep -i oom или просмотрите системный журнал journalctl -k | grep -i oom. При нехватке памяти в выводе появится сообщение вида Out of memory: Killed process <PID> (php-fpm).
5. Как включить страницу /fpm-status для мониторинга пула?
- В файле
www.confактивируйте параметр:pm.status_path = /fpm-status - В конфигурацию Nginx добавьте блок:
location = /fpm-status { include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/run/php/php8.2-fpm.sock; allow 127.0.0.1; deny all; } - После перезагрузки служб состояние пула проверяется вызовом
curl http://127.0.0.1/fpm-status?full.
Заключение
Бесперебойная работа связки Nginx и PHP-FPM строится на корректном распределении системных ресурсов и согласовании сетевых параметров. Для предотвращения большинства типовых инцидентов достаточно придерживаться трех базовых принципов:
- Рассчитывать значение
pm.max_childrenна основе реального объема RAM и среднего потребления памяти одним воркером. - Выстраивать сквозную иерархию таймаутов, где лимит Nginx превышает внутренние таймеры PHP.
- Использовать
slowlogи детальные журналы ошибок для упреждающего обнаружения блокировок и деградации производительности.



.svg.webp)





