15%

Сэкономьте 15% на всех хостинговых услугах

Проверьте свои навыки и получите скидку на любой тарифный план

Используйте код:

Skills
Начать
10.10.2024

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 важен, полезно увидеть, что именно он заменяет и почему эти альтернативы не справляются при масштабировании.

ФункцияCGImod_phpPHP-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: подробный путь запроса

Понимание внутреннего пути запроса необходимо для настройки и отладки.

  1. Браузер отправляет HTTP-запрос к ресурсу .php.
  2. Веб-сервер (NGINX или Apache) получает запрос и сопоставляет его с блоком location или директивой FilesMatch.
  3. Веб-сервер перенаправляет запрос в PHP-FPM через протокол FastCGI — либо через Unix domain socket (/run/php/php8.2-fpm.sock), либо через TCP-сокет (127.0.0.1:9000).
  4. Мастер-процесс PHP-FPM направляет запрос доступному рабочему процессу из настроенного пула.
  5. Рабочий процесс выполняет PHP-скрипт, записывает в stdout и возвращает ответ веб-серверу.
  6. Веб-сервер доставляет отрендеренный HTML клиенту.
  7. Рабочий процесс не завершается — он возвращается в пул ожидания, готовый к следующему запросу.

Unix-сокеты предпочтительнее TCP для локального взаимодействия, поскольку они полностью обходят стек TCP/IP, снижая задержку на 10–20% в тестах и устраняя накладные расходы на привязку к порту и маршрутизацию через loopback.

Режимы управления процессами

PHP-FPM поддерживает три режима pm (менеджера процессов), и выбор неправильного режима является одной из наиболее распространённых ошибок конфигурации.

pm = static

Фиксированное количество рабочих процессов всегда запущено, независимо от трафика. Используйте этот режим на выделенных серверах, где вам нужна предсказуемая, предварительно выделенная память и вы можете позволить себе накладные расходы на простой.

pm = static
pm.max_children = 20

pm = 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 = 500

pm = 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-fpm

CentOS / 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.php

PHP-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 настроен и доступен только с localhost
  • slowlog включён с request_slowlog_timeout 2–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-доступом.

15%

Сэкономьте 15% на всех хостинговых услугах

Проверьте свои навыки и получите скидку на любой тарифный план

Используйте код:

Skills
Начать