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-сокет домену (/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 kills; занадто низьке — до черги запитів і помилок 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 GB RAM, де NGINX, MySQL та ОС споживають ~600 MB, у вас є приблизно 1 400 MB для PHP. Якщо кожен воркер використовує ~70 MB, безпечне значення 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 вимикає перевірку змін файлів при кожному запиті — значний приріст продуктивності у виробничому середовищі. Коли ви розгортаєте новий код, явно запускайте скидання кешу:

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 і веб-сервера перевірте весь ланцюжок перед запуском у виробництво.

Перезавантажте всі служби:

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 у виробництві перевірте кожен пункт цього чеклиста:

Вибір менеджера процесів

  • Використовуйте 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 блокує функції виконання оболонки
  • Використовуються Unix-сокети замість TCP-сокетів

Спостережуваність

  • pm.status_path налаштовано і доступно лише з localhost
  • slowlog увімкнено зі значенням request_slowlog_timeout 2–5 секунд
  • Ротація логів налаштована для всіх файлів логів PHP-FPM

Продуктивність

  • OPcache увімкнено з validate_timestamps=0 у виробничому середовищі
  • 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 на правильний сокет для кожного серверного блоку. Це стандартна практика на спільній інфраструктурі, що управляється через Панелі керування VPS, і повністю підтримується на VPS Хостингу з root-доступом.

15%

Збережіть 15% на всі хостинг-послуги

Перевірте свої навички і отримайте Знижку на будь-який план хостингу

Використовуй код:

Skills
Почати