PHP-FPM (FastCGI Process Manager): Полное руководство по установке, настройке и оптимизации
PHP-FPM (PHP FastCGI Process Manager) — это высокопроизводительный альтернативный менеджер процессов PHP, реализующий протокол FastCGI для отделения выполнения PHP от процесса веб-сервера. Вместо того чтобы порождать новый интерпретатор PHP для каждого входящего HTTP-запроса — как это делает традиционный CGI — PHP-FPM поддерживает постоянный пул рабочих процессов, которые принимают, выполняют и возвращают PHP-ответы со значительно меньшими накладными расходами.
Для любого производственного веб-сервера, работающего с WordPress, Laravel, Symfony или пользовательскими PHP-приложениями, PHP-FPM является стандартным обработчиком. Он обеспечивает детальный контроль над жизненным циклом процессов, ограничениями памяти, очередями запросов и изоляцией на уровне приложений — возможности, которые просто недоступны при использовании mod_php или обычного CGI.
Чем PHP-FPM отличается от CGI и mod_php
Чтобы понять, почему PHP-FPM важен, полезно увидеть, что именно он заменяет и почему эти альтернативы не справляются при масштабировании.
| Функция | CGI | mod_php | PHP-FPM |
|---|
| — | — | — | — |
|---|
| Модель процессов | Новый процесс на каждый запрос | Встроен в Apache | Постоянный пул рабочих процессов |
|---|
| Эффективность памяти | Очень низкая | Умеренная | Отличная |
|---|
| Связанность с веб-сервером | Тесная | Тесная (только Apache) | Независимая (любой сервер) |
|---|
| Изоляция на уровне сайта | Отсутствует | Отсутствует | Полная (отдельные пулы) |
|---|
| Плавная перезагрузка | Нет | Нет | Да |
|---|
| Slow log / профилирование | Нет | Нет | Да |
|---|
| Динамическое масштабирование процессов | Нет | Нет | Да |
|---|
| Поддержка Unix-сокетов | Нет | Нет | Да |
|---|
| Совместимость с NGINX | Нет | Нет | Да |
|---|
CGI создаёт новый процесс ОС для каждого запроса. При умеренной нагрузке это порождает тысячи циклов fork/exec/exit в минуту, перегружая CPU и память. mod_php встраивает интерпретатор PHP непосредственно в каждый рабочий процесс Apache, то есть каждый процесс Apache — даже обслуживающий статическое изображение — несёт в памяти полную среду выполнения PHP. PHP-FPM решает обе проблемы: рабочие процессы постоянны и полностью отделены от веб-сервера, поэтому NGINX или Apache обрабатывают статические ресурсы нативно, а PHP-FPM занимается только выполнением PHP.
Архитектура PHP-FPM: подробный путь запроса
Понимание внутреннего пути запроса необходимо для настройки и отладки.
- Браузер отправляет HTTP-запрос к ресурсу
.php. - Веб-сервер (NGINX или Apache) получает запрос и сопоставляет его с блоком location или директивой
FilesMatch. - Веб-сервер перенаправляет запрос в PHP-FPM через протокол FastCGI — либо через Unix domain socket (
/run/php/php8.2-fpm.sock), либо через TCP-сокет (127.0.0.1:9000). - Мастер-процесс PHP-FPM направляет запрос доступному рабочему процессу из настроенного пула.
- Рабочий процесс выполняет PHP-скрипт, записывает в
stdoutи возвращает ответ веб-серверу. - Веб-сервер доставляет отрендеренный HTML клиенту.
- Рабочий процесс не завершается — он возвращается в пул ожидания, готовый к следующему запросу.
Unix-сокеты предпочтительнее TCP для локального взаимодействия, поскольку они полностью обходят стек TCP/IP, снижая задержку на 10–20% в тестах и устраняя накладные расходы на привязку к порту и маршрутизацию через loopback.
Режимы управления процессами
PHP-FPM поддерживает три режима pm (менеджера процессов), и выбор неправильного режима является одной из наиболее распространённых ошибок конфигурации.
pm = static
Фиксированное количество рабочих процессов всегда запущено, независимо от трафика. Используйте этот режим на выделенных серверах, где вам нужна предсказуемая, предварительно выделенная память и вы можете позволить себе накладные расходы на простой.
pm = static
pm.max_children = 20pm = dynamic
PHP-FPM запускает базовое количество рабочих процессов и масштабирует их вверх или вниз в заданных пределах. Это наиболее часто используемый режим и правильный вариант по умолчанию для большинства сред VPS Хостинга.
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500pm = ondemand
Рабочие процессы порождаются только при поступлении запроса и завершаются после pm.process_idle_timeout секунд бездействия. Это минимизирует потребление памяти в режиме ожидания и идеально подходит для сайтов с низким трафиком или общих сред, где сосуществуют десятки пулов.
pm = ondemand
pm.max_children = 20
pm.process_idle_timeout = 10sКритическая ошибка: ondemand вводит задержку холодного старта при первом запросе после периода простоя. Для приложений, чувствительных к задержкам, dynamic всегда является лучшим выбором.
Правильный расчёт pm.max_children
Именно здесь большинство администраторов допускают дорогостоящие ошибки. Слишком высокое значение pm.max_children приводит к исчерпанию памяти и завершению процессов по OOM; слишком низкое вызывает очередь запросов и ошибки 502 под нагрузкой.
Правильная формула:
pm.max_children = (Available RAM for PHP) / (Average PHP worker memory usage)Чтобы узнать среднее потребление памяти рабочим процессом PHP:
ps --no-headers -o "rss,cmd" -C php-fpm8.2 | awk '{ sum+=$1 } END { printf "Average: %d MBn", sum/NR/1024 }'На VPS с 2 ГБ RAM, где NGINX, MySQL и ОС потребляют ~600 МБ, у вас остаётся примерно 1 400 МБ для PHP. Если каждый рабочий процесс использует ~70 МБ, безопасное значение pm.max_children равно 20. Никогда не устанавливайте его наугад.
Установка PHP-FPM
Debian / Ubuntu
sudo apt update
sudo apt install php8.2-fpm
sudo systemctl enable php8.2-fpm
sudo systemctl start php8.2-fpmCentOS / AlmaLinux / RHEL (с репозиторием Remi)
sudo dnf install epel-release
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm
sudo dnf module enable php:remi-8.2
sudo dnf install php-fpm
sudo systemctl enable php-fpm
sudo systemctl start php-fpmУбедитесь, что служба запущена, и проверьте путь к сокету:
sudo systemctl status php8.2-fpm
ls -la /run/php/Настройка пулов PHP-FPM
Основная конфигурация PHP-FPM находится в /etc/php/8.2/fpm/php-fpm.conf, но определения отдельных пулов размещаются в /etc/php/8.2/fpm/pool.d/. На системах на базе RHEL файлы пулов находятся в /etc/php-fpm.d/.
Каждый пул является изолированной средой выполнения. Запуск нескольких PHP-приложений на одном сервере — например, сайта WordPress и Laravel API — означает создание отдельных файлов пулов с отдельными пользователями, путями к сокетам и ограничениями ресурсов. Это правильная архитектура для многопользовательских сред и значительно безопаснее, чем использование единого общего пула.
Пример: конфигурация производственного пула
[myapp]
user = myapp
group = myapp
; Unix socket — always prefer this over TCP for local communication
listen = /run/php/myapp-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
; Process manager
pm = dynamic
pm.max_children = 30
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
pm.max_requests = 1000
; Slow log — log requests taking longer than 2 seconds
slowlog = /var/log/php-fpm/myapp-slow.log
request_slowlog_timeout = 2s
; Status and ping endpoints
pm.status_path = /fpm-status
ping.path = /fpm-ping
; Environment isolation
clear_env = yes
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
; PHP value overrides per pool
php_admin_value[error_log] = /var/log/php-fpm/myapp-error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64MДиректива clear_env = yes является критически важной настройкой безопасности, которую часто упускают из виду. Без неё рабочие процессы PHP наследуют все переменные окружения от мастер-процесса, что может привести к утечке конфиденциальных системных данных в $_ENV вашего приложения.
Интеграция PHP-FPM с NGINX
NGINX не имеет встроенной возможности выполнения PHP — он полностью полагается на FastCGI для делегирования PHP-запросов. Это фактически является архитектурным преимуществом: NGINX обрабатывает статические файлы практически без затрат, а PHP-FPM обрабатывает только то, что требует выполнения.
server {
listen 80;
server_name example.com;
root /var/www/myapp/public;
index index.php index.html;
# Serve static files directly, no PHP involvement
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Delegate PHP to PHP-FPM
location ~ .php$ {
# Security: prevent executing uploaded files as PHP
try_files $uri =404;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass unix:/run/php/myapp-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
# Performance tuning
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_read_timeout 300;
}
# Block access to the FPM status page from public
location ~ ^/(fpm-status|fpm-ping)$ {
allow 127.0.0.1;
deny all;
fastcgi_pass unix:/run/php/myapp-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}Примечание по безопасности: Строка try_files $uri =404; перед fastcgi_pass является обязательной. Без неё NGINX будет перенаправлять запросы к несуществующим файлам в PHP-FPM, что открывает возможность атак с обходом пути, когда злоумышленник загружает изображение, содержащее PHP-код, и обманом заставляет сервер его выполнить.
Интеграция PHP-FPM с Apache
Apache требует mod_proxy_fcgi для взаимодействия с PHP-FPM. В отличие от mod_php, этот подход позволяет Apache запускать PHP-FPM от имени отдельного пользователя, улучшая изоляцию.
sudo a2enmod proxy_fcgi setenvif
sudo systemctl restart apache2Конфигурация виртуального хоста:
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/myapp/public
<Directory /var/www/myapp/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<FilesMatch ".php$">
SetHandler "proxy:unix:/run/php/myapp-fpm.sock|fcgi://localhost/"
</FilesMatch>
ErrorLog ${APACHE_LOG_DIR}/myapp-error.log
CustomLog ${APACHE_LOG_DIR}/myapp-access.log combined
</VirtualHost>Включение и использование страницы статуса PHP-FPM
Встроенная страница статуса является одним из наиболее недооценённых диагностических инструментов PHP-FPM. После настройки pm.status_path в файле пула запросите её напрямую:
sudo -u www-data SCRIPT_NAME=/fpm-status SCRIPT_FILENAME=/fpm-status REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/myapp-fpm.sockИли, что более практично, через curl после открытия доступа на ограниченном внутреннем эндпоинте:
curl http://127.0.0.1/fpm-status?fullКлючевые метрики для наблюдения:
listen queue: Запросы, ожидающие свободного рабочего процесса. Любое значение выше 0 при устойчивой нагрузке означает, чтоpm.max_childrenслишком мало.active processes: Рабочие процессы, в данный момент выполняющие PHP. Если это значение постоянно равноpm.max_children, вы достигли предела мощности.slow requests: Накопленное количество запросов, превысившихrequest_slowlog_timeout. Растущее значение указывает на узкие места на уровне приложения.
Использование slow log для отладки производительности
Slow log фиксирует полную трассировку стека PHP для любого запроса, превышающего настроенный порог. Это бесценно для выявления проблем N+1 запросов, блокирующих вызовов I/O или неэффективных циклов без необходимости использования полноценного профилировщика.
slowlog = /var/log/php-fpm/myapp-slow.log
request_slowlog_timeout = 2sЗапись в slow log выглядит следующим образом:
[21-Jun-2025 14:32:11] [pool myapp] pid 18432
script_filename = /var/www/myapp/public/index.php
[0x00007f3b4c001e80] PDOStatement->execute() /var/www/myapp/vendor/laravel/framework/src/Illuminate/Database/Connection.php:338
[0x00007f3b4c001d40] runQueryCallback() /var/www/myapp/vendor/laravel/framework/src/Illuminate/Database/Connection.php:295Это немедленно указывает на то, что узким местом является запрос к базе данных, а не логика PHP — точно направляя ваши усилия по оптимизации.
PHP-FPM с OPcache: необходимая связка
PHP-FPM сам по себе управляет процессами; OPcache устраняет затраты на разбор и компиляцию PHP-файлов при каждом запросе. Вместе они образуют полный стек производительности для PHP на Linux.
Включите и настройте OPcache в /etc/php/8.2/fpm/php.ini или в выделенном файле /etc/php/8.2/fpm/conf.d/10-opcache.ini:
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.revalidate_freq=0
opcache.validate_timestamps=0
opcache.jit_buffer_size=128M
opcache.jit=tracingУстановка validate_timestamps=0 отключает проверку изменений файлов при каждом запросе — значительный прирост производительности в production-среде. При развёртывании нового кода явно сбрасывайте кэш:
sudo systemctl reload php8.2-fpmНа VPS с cPanel настройки OPcache часто доступны в интерфейсе конфигурации PHP, однако ручная настройка через файлы .ini всегда обеспечивает более тонкий контроль.
Усиление безопасности PHP-FPM
Запускайте каждый пул от имени выделенного системного пользователя
Никогда не запускайте пулы PHP-FPM от имени root или общего пользователя www-data для нескольких приложений. Создайте выделенного системного пользователя для каждого приложения:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myappЗатем установите user = myapp и group = myapp в конфигурации пула. Это гарантирует, что скомпрометированное PHP-приложение не сможет читать файлы, принадлежащие другим приложениям на том же сервере.
Ограничьте PHP-функции
В блоке php_admin_value пула отключите функции, не имеющие законного применения в веб-приложениях:
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_sourceОграничьте open_basedir
Ограничьте доступ PHP к файлам директорией приложения:
php_admin_value[open_basedir] = /var/www/myapp:/tmpИспользуйте Unix-сокеты со строгими правами доступа
TCP-сокеты (127.0.0.1:9000) доступны любому процессу на сервере. Unix-сокеты с listen.mode = 0660 ограничивают доступ только владельцем и его группой.
Проверка полного стека
После настройки PHP-FPM и веб-сервера проверьте всю цепочку перед запуском в production.
Перезагрузите все службы:
sudo systemctl reload php8.2-fpm
sudo systemctl reload nginx
# or
sudo systemctl reload apache2Проверьте синтаксис конфигурации NGINX перед перезагрузкой:
sudo nginx -tСоздайте временный информационный файл (удалите его после проверки — он раскрывает конфиденциальные данные сервера):
echo "<?php phpinfo();" | sudo tee /var/www/myapp/public/phpinfo.phpОткройте http://example.com/phpinfo.php в браузере и убедитесь, что:
- Server API отображает
FPM/FastCGI - PHP Version соответствует установленной версии
- Раздел OPcache присутствует и включён
Затем немедленно удалите файл:
sudo rm /var/www/myapp/public/phpinfo.phpPHP-FPM в многоприложенческих и высоконагруженных средах
На Выделенном сервере, обслуживающем десятки PHP-приложений, архитектура с несколькими пулами становится необходимой. Каждое приложение получает собственный пул с независимо настроенными pm.max_children, ограничениями памяти и путями к slow log. Неисправное приложение, исчерпавшее свой пул рабочих процессов, не влияет на другие приложения.
Для сценариев с высоким трафиком объедините PHP-FPM с:
- Кэшированием NGINX FastCGI (
fastcgi_cache) для обслуживания кэшированных PHP-ответов как статических файлов, полностью минуя PHP-FPM для повторных запросов - Redis или Memcached для хранения PHP-сессий, заменяя стандартные файловые сессии, которые создают конкуренцию I/O под нагрузкой
- Горизонтальным масштабированием путём запуска PHP-FPM на серверах приложений за балансировщиком нагрузки с NGINX на отдельном фронтальном узле
Если ваш стек включает терминирование SSL, сочетание PHP-FPM с правильно настроенными SSL-сертификатами на уровне NGINX гарантирует, что TLS-рукопожатия обрабатываются до того, как запросы достигнут PHP-FPM, позволяя рабочим процессам PHP сосредоточиться исключительно на логике приложения.
Для вычислительно интенсивных PHP-нагрузок — вывода машинного обучения через PHP-привязки, обработки изображений или перекодирования видео — рассмотрите GPU Хостинг, где PHP-FPM может делегировать тяжёлые вычисления GPU-ускоренным библиотекам, сохраняя стандартную обработку запросов для веб-уровня.
Матрица ключевых решений и технический чеклист
Перед развёртыванием PHP-FPM в production проверьте каждый пункт этого чеклиста:
Выбор менеджера процессов
- Используйте
pm = dynamicдля универсальных VPS-нагрузок - Используйте
pm = staticтолько на выделенных серверах с предсказуемым, устойчивым трафиком - Используйте
pm = ondemandтолько для пулов с низким трафиком или разработки
Планирование мощности
- Измерьте фактическое потребление памяти рабочим процессом с помощью
psперед установкойpm.max_children - Зарезервируйте не менее 20% общей RAM для ОС, веб-сервера и базы данных
- Установите
pm.max_requestsв диапазоне 500–1000 для предотвращения накопления утечек памяти
Безопасность
- Каждый пул приложения запускается от имени собственного системного пользователя
clear_env = yesустановлен в каждом пулеopen_basedirограничивает доступ к файлам директорией приложенияdisable_functionsблокирует функции выполнения shell-команд- Используются Unix-сокеты вместо TCP-сокетов
Наблюдаемость
pm.status_pathнастроен и доступен только с localhostslowlogвключён сrequest_slowlog_timeout2–5 секунд- Ротация логов настроена для всех лог-файлов PHP-FPM
Производительность
- OPcache включён с
validate_timestamps=0в production-среде - Кэширование NGINX FastCGI настроено для кэшируемых эндпоинтов
- Обработчик PHP-сессий настроен на Redis или Memcached, а не на файлы
Операционные аспекты
sudo systemctl reload php8.2-fpmиспользуется для изменений конфигурации без простоя (неrestart)phpinfo.phpудалён из корневой директории документов сразу после проверки- Конфигурация пула хранится в системе контроля версий вместе с кодом приложения
FAQ
В чём разница между PHP-FPM и mod_php?
mod_php встраивает интерпретатор PHP в каждый рабочий процесс Apache, потребляя память даже при обслуживании статических файлов и тесно связывая PHP с Apache. PHP-FPM работает как полностью отдельная служба, взаимодействует через FastCGI, работает с любым веб-сервером, включая NGINX, и обеспечивает изоляцию процессов на уровне приложений с независимыми ограничениями ресурсов.
Как выбрать между Unix-сокетом и TCP-сокетом для PHP-FPM?
Используйте Unix-сокет (listen = /run/php/app-fpm.sock), когда PHP-FPM и веб-сервер работают на одной физической или виртуальной машине. Unix-сокеты обходят стек TCP/IP, снижая задержку и устраняя конфликты портов. Используйте TCP-сокет (listen = 127.0.0.1:9000) только когда PHP-FPM работает на другом хосте, нежели веб-сервер.
Почему я получаю ошибки 502 Bad Gateway под нагрузкой?
Ошибка 502 от NGINX, указывающая на PHP-FPM, почти всегда означает, что очередь прослушивания заполнена — все рабочие процессы заняты и новые соединения отклоняются. Проверьте pm.status_path на ненулевое значение listen queue. Решение — либо увеличить pm.max_children (если позволяет RAM), либо оптимизировать медленные PHP-скрипты, выявленные через slow log.
Как перезагрузить PHP-FPM без потери активных соединений?
Используйте sudo systemctl reload php8.2-fpm вместо restart. Сигнал reload (SIGUSR2) заставляет мастер-процесс плавно перезапускать рабочие процессы: текущие запросы завершаются нормально, а новые рабочие процессы подхватывают обновлённую конфигурацию. Жёсткий restart немедленно завершает все рабочие процессы, прерывая выполняющиеся запросы.
Может ли PHP-FPM одновременно запускать несколько версий PHP на одном сервере?
Да. Установите несколько версий PHP (например, php7.4-fpm и php8.2-fpm) и настройте каждый пул приложения на использование соответствующего пути к сокету. В NGINX укажите fastcgi_pass на правильный сокет для каждого блока server. Это стандартная практика на общей инфраструктуре, управляемой через Панели управления VPS, и полностью поддерживается на VPS Хостинге с root-доступом.
