Коротко: боевая связка PHP — Nginx как фронт и PHP-FPM по FastCGI через unix-сокет; Nginx отдаёт статику, FPM держит пул воркеров.
Боевая связка для PHP-сайта — это Nginx как фронт и PHP-FPM как процесс-менеджер интерпретатора, общающиеся по FastCGI через unix-сокет. Nginx отдаёт статику и проксирует только PHP-запросы в FPM; FPM держит пул воркеров. Разберу минимально-достаточный production-конфиг и типовые грабли.
Почему именно Nginx + PHP-FPM
Nginx прекрасно и дёшево отдаёт статику и держит тысячи соединений, но сам PHP не исполняет. PHP-FPM (FastCGI Process Manager) управляет пулом PHP-процессов. Связка получается такой: статика (css/js/images) — отдаёт Nginx напрямую с диска, а запросы к index.php уходят в FPM. Это быстрее и стабильнее, чем mod_php в Apache.
Конфиг виртуального хоста Nginx
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example/public; # public/, а не корень проекта!
index index.php;
# статику отдаём напрямую, с кэшем
location ~* \.(?:css|js|jpg|jpeg|png|gif|ico|svg|woff2?)$ {
expires 30d;
access_log off;
try_files $uri =404;
}
# фронт-контроллер Symfony: всё на index.php
location / {
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
internal; # прямой доступ к /index.php извне запрещён
}
# запрещаем исполнять любые другие .php (защита от залитых файлов)
location ~ \.php$ { return 404; }
}
Пул PHP-FPM
FPM настраивается отдельно (например, /etc/php/8.4/fpm/pool.d/www.conf). Ключевое — модель процессов и их число:
; общение через unix-сокет (быстрее TCP на одной машине)
listen = /run/php/php8.4-fpm.sock
listen.owner = www-data
listen.group = www-data
; динамический пул
pm = dynamic
pm.max_children = 20 ; потолок процессов (упирается в RAM)
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500 ; перезапуск воркера, лечит утечки памяти
Главная ошибка — задрать pm.max_children «на всякий случай». Считайте от памяти: (свободная RAM) / (средняя память одного процесса). Если процессов больше, чем сервер тянет, при нагрузке начнётся своппинг и всё ляжет.
Production-минимум, который часто забывают
- root указывает на
public/, а не на корень проекта — иначе наружу торчат.env,vendor/, конфиги. - Запрет исполнения произвольных
.php(см. конфиг) — чтобы залитая через дыру картинка-«шелл» не выполнилась. - В
php.iniдля прода:display_errors = Off,expose_php = Off, включён OPcache. - OPcache — кэш скомпилированного байткода, на проде даёт кратный прирост; в продакшене
opcache.validate_timestamps=0(сброс кэша только при деплое). - Таймауты: согласуйте
fastcgi_read_timeoutв Nginx иmax_execution_timeв PHP.
Как проверить и перезапустить
# проверить синтаксис конфигов перед рестартом
nginx -t
php-fpm8.4 -t
# применить
systemctl reload nginx
systemctl restart php8.4-fpm
# посмотреть, что сокет на месте и слушается
ss -lx | grep php8.4-fpm.sock
Итог
Базовая боевая связка несложная: Nginx отдаёт статику и проксирует PHP в пул FPM, root смотрит в public/, произвольные .php запрещены, число воркеров посчитано от памяти, OPcache включён. Этого достаточно для уверенного старта большинства PHP-проектов.
Если нужно настроить или вычистить боевое окружение под нагрузку — помогу на консультации.
Когда сервер настроен, важно видеть, что на нём реально происходит: ошибки, аптайм и метрики удобно собирать self-hosted — например, через Gotcha, не платя за облачный мониторинг.

Комментарии