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-сокет домену (
/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 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-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 вимикає перевірку змін файлів при кожному запиті — значний приріст продуктивності у виробничому середовищі. Коли ви розгортаєте новий код, явно запускайте скидання кешу:
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.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 у виробництві перевірте кожен пункт цього чеклиста:
Вибір менеджера процесів
- Використовуйте
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налаштовано і доступно лише з localhostslowlogувімкнено зі значеннямrequest_slowlog_timeout2–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-доступом.
