Связка Nginx + PHP-FPM с нуля
Пошаговая настройка Nginx и PHP-FPM для сайта на PHP: от установки до тонкой настройки пула процессов, таймаутов и разбора типичных причин ошибки 502 Bad Gateway.
Зачем разделять веб-сервер и обработчик PHP
Nginx, в отличие от Apache с mod_php, не умеет исполнять PHP-код самостоятельно — он передаёт такие запросы отдельному процессу PHP-FPM (FastCGI Process Manager) по протоколу FastCGI. Это разделение на первый взгляд усложняет конфигурацию, но даёт заметное практическое преимущество: Nginx отдаёт статические файлы (изображения, CSS, JS) напрямую, без обращения к PHP вообще, а к PHP-FPM обращается только тогда, когда запрос действительно требует выполнения кода.
PHP-FPM управляет пулом рабочих процессов PHP, которые обрабатывают запросы параллельно, — в отличие от старого CGI, который запускал новый процесс интерпретатора на каждый запрос с нуля, PHP-FPM держит процессы уже готовыми к работе, что значительно ускоряет обработку под нагрузкой.
FastCGI Process Manager был создан именно для того, чтобы решить проблемы производительности и стабильности, свойственные классическому CGI, при этом оставаясь независимым от конкретного веб-сервера.
Из документации проекта PHP-FPM
Эта статья пошагово разбирает настройку связки с нуля: установку обоих компонентов, конфигурацию пула PHP-FPM, конфигурацию сайта в Nginx, тонкую настройку производительности через pm-параметры и таймауты, а также самые частые причины ошибки 502 Bad Gateway, с которой сталкивается почти каждый, кто настраивает эту связку впервые.
Разница между этой связкой и более простым mod_php на Apache подробно разобрана в отдельной статье базы знаний — если вопрос выбора веб-сервера ещё не решён, разумно сначала прочитать сравнение, а затем вернуться к этому пошаговому руководству уже с осознанным выбором в пользу Nginx.
Установка Nginx и PHP-FPM
На Ubuntu и Debian оба компонента устанавливаются из стандартных репозиториев одной командой каждый — конкретную версию PHP стоит выбирать исходя из требований CMS, а не брать automatически самую новую, если проект пока не тестировался с ней.
apt update
apt install nginx php8.3-fpm -y
Дополнительные расширения PHP, нужные конкретной CMS (работа с изображениями, базой данных, кэшем), устанавливаются отдельными пакетами и автоматически подключаются к уже установленному PHP-FPM без дополнительной ручной настройки конфигурации после простого перезапуска службы.
apt install php8.3-mysql php8.3-gd php8.3-mbstring php8.3-curl php8.3-zip php8.3-opcache -y
systemctl restart php8.3-fpm
Конфигурация пула PHP-FPM
PHP-FPM группирует рабочие процессы в пулы — каждый пул описывается отдельным конфигурационным файлом (обычно в /etc/php/8.3/fpm/pool.d/) и может обслуживать один или несколько сайтов с общими настройками пользователя, лимитов и способа связи с веб-сервером.
[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
Параметр listen задаёт способ связи с Nginx — здесь используется unix-сокет, что стандартно для конфигурации на одном сервере. Пользователь и группа пула должны совпадать с пользователем, от имени которого работает Nginx (обычно www-data), иначе Nginx не сможет писать в сокет и запросы будут падать с ошибкой доступа к файлу сокета.
Режимы pm: static, dynamic, ondemand
Параметр pm определяет стратегию управления количеством рабочих процессов. Режим static держит фиксированное число процессов постоянно запущенными — самый предсказуемый по расходу памяти вариант, подходящий для сервера, где заранее известна стабильная нагрузка.
Режим dynamic (использован в примере выше) поддерживает переменное число процессов между pm.min_spare_servers и pm.max_spare_servers, стартуя с pm.start_servers и создавая новые процессы по мере роста нагрузки, — разумный выбор по умолчанию для большинства сайтов с непостоянной посещаемостью в течение суток.
Режим ondemand создаёт процессы только при поступлении запроса и завершает их после периода бездействия — экономит память на простаивающем сервере ценой небольшой задержки на старте нового процесса под первый запрос после паузы; подходит для серверов с редкими всплесками трафика и жёстким ограничением по RAM.
Как рассчитать pm.max_children под конкретный сервер
Формула для оценки: доступная память под PHP-FPM (обычно общая RAM сервера минус память под систему, веб-сервер, базу данных и системный запас) делится на средний расход памяти одним PHP-процессом под конкретную CMS.
ps --no-headers -C php-fpm8.3 -o rss | awk '{sum+=$1; n++} END {print sum/n/1024, "MB на процесс"}'
Например, при 2 ГБ памяти, выделенной под PHP-FPM, и среднем расходе 60 МБ на процесс, разумный ориентир для pm.max_children — около 30–33 процессов, с небольшим запасом вниз для системного буфера и других служб на сервере. Завышенное значение не ускорит обработку, а лишь позволит системе уйти в своп при попытке создать больше процессов, чем реально помещается в RAM, что снижает производительность вместо того, чтобы её повышать.
Слишком высокий pm.max_children — это не запас прочности, а отложенный OOM Killer: сервер честно попытается создать столько процессов, сколько указано, даже если для этого не хватит памяти.
Частое наблюдение в практике тюнинга PHP-FPM
Параметры pm.start_servers, pm.min_spare_servers и pm.max_spare_servers имеют смысл только в режиме dynamic и определяют, сколько процессов держать запущенными про запас между всплесками нагрузки — слишком низкие значения приводят к заметной задержке при резком росте трафика, поскольку новые процессы PHP-FPM создаются не мгновенно, а слишком высокие бессмысленно расходуют память на простаивающие процессы в моменты низкой посещаемости.
Конфигурация сайта в Nginx
Server-блок Nginx для сайта на PHP-FPM должен явно указывать, что запросы к файлам .php передаются через FastCGI на сокет, заданный в конфигурации пула, — путь к сокету в обеих конфигурациях должен совпадать буквально, иначе Nginx не найдёт PHP-FPM и вернёт 502.
server {
listen 80;
server_name example.ru;
root /var/www/example.ru/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
}
Блок location ~ /\.ht запрещает доступ к файлам, начинающимся на .ht (включая .htaccess, если он случайно оказался в директории сайта после переноса с Apache) — Nginx их не использует, но их наличие в публичной директории может случайно раскрыть служебную информацию, если явно не заблокировать к ним доступ.
Таймауты: где сайт может зависнуть, а где — оборваться раньше времени
Таймауты в связке Nginx и PHP-FPM настраиваются в двух независимых местах, и рассинхронизация между ними — частый источник труднообъяснимых обрывов долгих запросов. В Nginx за это отвечает fastcgi_read_timeout, ограничивающий время ожидания ответа от PHP-FPM.
fastcgi_read_timeout 60s;
В самом PHP-FPM за похожее поведение отвечает request_terminate_timeout в конфигурации пула — если он меньше, чем fastcgi_read_timeout в Nginx, PHP-FPM прервёт выполнение скрипта раньше, чем Nginx успеет посчитать это таймаутом на своей стороне, что может приводить к неполным или пустым ответам вместо явной ошибки.
request_terminate_timeout = 60s
Практическая рекомендация — держать оба значения синхронизированными и достаточными для самых тяжёлых легитимных операций сайта (например, генерация большого отчёта или импорт каталога), но не бесконечными: без разумного таймаута зависший процесс будет занимать слот в pm.max_children неограниченно долго, лишая ресурсов остальные запросы к тому же самому серверу.
Логи и slow log: как найти конкретную медленную функцию
Обычный error-лог PHP-FPM показывает фатальные ошибки и предупреждения, но не покажет, какая именно строка кода стала причиной, если запрос просто выполняется слишком долго без явной ошибки. Для этого в PHP-FPM есть отдельный механизм — slow log, включаемый двумя параметрами в конфигурации пула.
request_slowlog_timeout = 5s
slowlog = /var/log/php8.3-fpm-slow.log
При срабатывании slow log записывает полный стек вызовов PHP на момент истечения таймаута — то есть показывает, в какой именно функции и на какой строке кода застрял медленный запрос, что избавляет от необходимости добавлять отладочные вызовы времени выполнения вручную по всему коду в поисках узкого места.
Разумная практика — включать slow log с порогом чуть ниже типичного request_terminate_timeout на время диагностики конкретной проблемы с производительностью, а не держать его активным постоянно с очень низким порогом, поскольку это создаёт дополнительную нагрузку на диск при интенсивной записи логов.
Отдельно стоит настроить ротацию файлов логов PHP-FPM и Nginx через logrotate — без неё лог-файлы будут неограниченно расти на диске, что на серверах с небольшим объёмом хранилища рано или поздно приведёт к его исчерпанию и остановке сайта по совершенно неожиданной причине, никак не связанной с самим кодом приложения.
Практический пример: полная настройка под WordPress
Для сайта на WordPress типичный набор корректировок к базовой конфигурации включает увеличенные лимиты на размер загружаемых файлов и время выполнения для операций вроде импорта контента или обновления плагинов, которые могут занимать больше времени, чем обычный просмотр страницы.
; в php.ini или отдельном .ini-файле пула
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 120
memory_limit = 256M
Эти значения можно задать либо глобально в php.ini для всего пула, либо точечно для конкретного сайта через параметр php_admin_value непосредственно в конфигурации пула — второй вариант удобен, когда на одном сервере несколько сайтов с разными требованиями к лимитам.
php_admin_value[upload_max_filesize] = 64M
php_admin_value[memory_limit] = 256M
OPcache: ещё один слой производительности, о котором легко забыть
PHP по умолчанию компилирует скрипт в промежуточный байт-код заново на каждый запрос — при высокой посещаемости это заметная и совершенно ненужная трата CPU, если сам код между запросами не менялся. Расширение OPcache кэширует уже скомпилированный байт-код в разделяемой памяти и переиспользует его для последующих запросов, пропуская этап компиляции целиком.
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=2
Параметр validate_timestamps=1 в связке с revalidate_freq определяет, как часто PHP проверяет, не изменился ли файл на диске, чтобы обновить кэш, — на активно разрабатываемом проекте эту проверку стоит оставить включённой; для production-сервера с редкими деплоями через CI/CD часть администраторов отключает её вовсе (validate_timestamps=0) ради дополнительной скорости, но тогда обновление кода требует явного сброса кэша OPcache после каждого деплоя.
OPcache — одно из немногих улучшений производительности PHP, которое буквально ничего не стоит с точки зрения сложности кода: включил, выделил разумный объём памяти, и получил измеримое ускорение без единой строчки изменений в самом приложении.
Распространённая рекомендация в руководствах по тюнингу PHP-стека
Мониторинг статуса PHP-FPM через встроенную страницу
PHP-FPM умеет отдавать собственную страницу статуса с текущими метриками пула — числом активных и простаивающих процессов, количеством обработанных запросов, длиной очереди ожидания. Эта информация значительно упрощает диагностику того, действительно ли текущих настроек pm достаточно, вместо того чтобы гадать по косвенным признакам.
pm.status_path = /fpm-status
После добавления этого параметра в конфигурацию пула и соответствующего location-блока в Nginx, ограниченного доступом только с localhost или конкретного IP из соображений безопасности, страница статуса покажет в реальном времени, насколько близко текущая нагрузка подошла к пределу pm.max_children — это гораздо надёжнее, чем реагировать на проблему только по факту появления ошибок 502 у реальных посетителей.
Итоговая рекомендация — не настраивать pm-параметры и таймауты один раз и забывать о них навсегда, а периодически возвращаться к странице статуса PHP-FPM и логам slow log по мере роста проекта, пересчитывая значения под изменившийся профиль нагрузки конкретного сайта.
Типичные причины ошибки 502 Bad Gateway
Служба PHP-FPM не запущена или упала после сбоя — простейшая, но частая причина; проверяется командой systemctl status php8.3-fpm, а перезапуск — systemctl restart php8.3-fpm.
Путь к сокету в конфигурации Nginx не совпадает с путём в конфигурации пула PHP-FPM — например, из-за смены версии PHP без обновления пути в fastcgi_pass. Проверить реальный путь можно, посмотрев файл сокета в директории /run/php/.
Достигнут лимит pm.max_children, и все процессы заняты обработкой других запросов — в логе PHP-FPM в этом случае появляется характерное сообщение "server reached pm.max_children setting, consider raising it".
Несовпадение прав пользователя и группы между Nginx и пулом PHP-FPM для unix-сокета — Nginx физически не может писать в сокет, если процессы работают от разных пользователей без прав доступа друг к другу.
Скрипт PHP превысил memory_limit или request_terminate_timeout и был принудительно завершён PHP-FPM до того, как успел сформировать ответ — это часто сопровождается конкретной записью в error-логе PHP-FPM с указанием причины завершения.
Файловая система переполнена или превышена квота на количество inode — PHP-FPM физически не может создать новый процесс или временный файл сессии, что внешне выглядит как случайные обрывы соединения без явной закономерности; проверяется командами df -h и df -i.
Чек-лист настройки связки с нуля
Шаг 1. Установите Nginx и PHP-FPM нужной версии вместе с расширениями, требуемыми конкретной CMS. Шаг 2. Настройте пул PHP-FPM: пользователя, группу, путь к сокету и режим pm под ожидаемую нагрузку сервера.
Шаг 3. Настройте server-блок Nginx с точным путём к тому же сокету, что указан в конфигурации пула, и проверьте конфигурацию командой nginx -t перед перезапуском.
Шаг 4. Синхронизируйте таймауты fastcgi_read_timeout в Nginx и request_terminate_timeout в PHP-FPM под реальные потребности проекта, не оставляя их бесконечными.
Шаг 5. Настройте лимиты memory_limit, upload_max_filesize и max_execution_time под требования конкретной CMS, глобально или точечно для отдельного пула.
Шаг 6. Проверьте работу связки, открыв сайт в браузере и убедившись, что PHP-код действительно выполняется (например, через phpinfo() на тестовой странице), а не отдаётся как обычный текстовый файл.
Шаг 7. При необходимости включите slow log для диагностики медленных запросов и настройте мониторинг лога PHP-FPM на сообщения о достижении pm.max_children.
Итог: разделение ответственности, которое окупается на нагрузке
Связка Nginx и PHP-FPM требует немного больше первоначальной настройки по сравнению с более простым mod_php на Apache, но эта дополнительная сложность окупается на реальной нагрузке: статика отдаётся без участия PHP вообще, процессы PHP-FPM управляются отдельно и гибко настраиваются под конкретный профиль нагрузки конкретного проекта.
Большинство проблем с этой связкой сводится к нескольким типичным причинам — несовпадению путей к сокету, нехватке pm.max_children или рассинхронизации таймаутов — и быстро диагностируется по логам обоих компонентов при понимании того, как они взаимодействуют друг с другом. Если настройка сервера с нуля кажется избыточно технической задачей — оставьте заявку внизу страницы, настроим связку Nginx и PHP-FPM под конкретный стек и профиль нагрузки вашего проекта.
Освоив эту связку один раз, дальнейшая работа с ней становится рутинной: добавление нового сайта на сервер сводится к копированию шаблонного server-блока с изменёнными путями и доменным именем, а масштабирование под рост нагрузки — к пересчёту pm.max_children по уже знакомой формуле и, при необходимости, переходу на более мощную конфигурацию VPS.
Почему стоит выбрать нас
Быстрый запуск
Подберём конфигурацию и развернём проект в день обращения.
Надёжность
NVMe-диски, защита от DDoS и резервные копии по расписанию.
Поддержка 24/7
Дежурные инженеры на связи в любое время суток.
Настройка под ключ
Установим CMS, настроим веб-сервер, SSL и почту за вас.
Перенос без простоя
Аккуратно перенесём сайт и базу данных на новый сервер.
Прозрачные цены
Актуальные тарифы и честный подбор без навязывания.
Вопросы и ответы
Почему сайт отдаёт ошибку 502 Bad Gateway именно с этой связкой?
Чаще всего причина в том, что Nginx не может достучаться до PHP-FPM: сервис PHP-FPM не запущен, путь к сокету в конфигурации Nginx не совпадает с реальным путём в конфигурации пула, или все процессы PHP-FPM заняты и новые запросы упираются в лимит pm.max_children.
Что лучше использовать — unix-сокет или TCP-порт для связи Nginx и PHP-FPM?
Unix-сокет немного быстрее за счёт меньших накладных расходов на передачу данных и является стандартным выбором, когда Nginx и PHP-FPM работают на одном сервере; TCP-порт нужен, только если PHP-FPM вынесен на отдельный сервер.
Как выбрать значение pm.max_children?
Разделите объём памяти, выделенной под PHP-FPM, на средний расход памяти одним процессом PHP (обычно 30–80 МБ в зависимости от CMS) — получившееся число и есть разумный верхний предел; точное значение стоит уточнять по факту нагрузки через мониторинг.
Нужно ли перезапускать Nginx после изменения конфигурации PHP-FPM?
Нет, перезапускать нужно именно службу PHP-FPM (systemctl restart php8.3-fpm); Nginx достаточно перезагрузить только если менялась его собственная конфигурация, например путь к сокету.
Можно ли запускать несколько версий PHP одновременно на одном сервере?
Да, это одно из практических преимуществ связки Nginx с PHP-FPM: разные пулы PHP-FPM разных версий слушают разные сокеты, и в конфигурации каждого сайта в Nginx можно указать нужную версию через fastcgi_pass — это удобно, когда на одном сервере уживаются проекты с разными требованиями к PHP.
Стоит ли отключать validate_timestamps в OPcache на production?
Это ускоряет сайт за счёт пропуска проверки изменения файлов, но требует ручного сброса кэша OPcache после каждого деплоя нового кода — разумно только при налаженном процессе автоматического деплоя, который включает этот шаг явно.
Что такое slow log в PHP-FPM и зачем он нужен?
Это отдельный лог, куда PHP-FPM записывает трассировку стека для запросов, которые выполняются дольше заданного порога (request_slowlog_timeout), — незаменимый инструмент для поиска конкретной медленной функции в коде без необходимости профилировать весь сайт целиком.
Ускоряет ли OPcache сайт заметно на практике?
Да, для типичного PHP-приложения на CMS выигрыш от включённого OPcache обычно измеряется десятками процентов по времени генерации страницы, поскольку исчезает необходимость заново разбирать и компилировать один и тот же PHP-код на каждый запрос.
Как понять, что текущих настроек pm уже не хватает?
Если в логе PHP-FPM регулярно появляются сообщения вида "server reached max_children", а сайт под нагрузкой периодически отдаёт 502 или подвисает — это прямой сигнал либо увеличить pm.max_children при наличии свободной памяти, либо перейти на более мощную конфигурацию сервера.
Нужно ли включать OPcache отдельно от установки PHP-FPM?
В большинстве современных сборок PHP расширение OPcache устанавливается вместе с самим PHP, но по умолчанию может быть не включено или настроено с параметрами, не рассчитанными на production-нагрузку, — стоит явно проверить и задать opcache.enable=1 и разумный объём памяти под конкретный проект.
Что делать, если страница статуса PHP-FPM доступна кому угодно в интернете?
Немедленно ограничить доступ к ней в конфигурации Nginx правилом allow/deny по IP-адресу — эта страница раскрывает внутренние детали инфраструктуры сервера, которые не должны быть видны посторонним, и в отдельных случаях облегчают злоумышленнику подбор момента для атаки на исчерпание пула процессов.
Как оплачивать услуги?
Оплата помесячная, для облачных серверов доступна почасовая тарификация. Принимаются карты, банковские переводы и электронные кошельки.
Есть ли круглосуточная поддержка?
Да, дежурные инженеры на связи 24/7.
Выдаёте ли закрывающие документы?
Да, предоставим необходимые документы для оплаты.
Как оставить заявку?
Заполните форму на странице или нажмите «Заказать» — мы свяжемся и поможем с подбором.