Главная / База знаний / Связка Nginx + PHP-FPM с нуля
инструкция · 15 сентября 2026

Связка Nginx + PHP-FPM с нуля

Пошаговая настройка Nginx и PHP-FPM для сайта на PHP: от установки до тонкой настройки пула процессов, таймаутов и разбора типичных причин ошибки 502 Bad Gateway.

Зачем разделять веб-сервер и обработчик PHP

Как запрос проходит через связку Nginx и PHP-FPM: статические файлы отдаются напрямую Nginx, динамические PHP-запросы передаются через сокет пулу процессов PHP-FPM

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ически самую новую, если проект пока не тестировался с ней.

Установка Nginx и PHP-FPM версии 8.3 на Ubuntu/Debian
apt update
apt install nginx php8.3-fpm -y

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

Установка типичного набора расширений и перезапуск 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/) и может обслуживать один или несколько сайтов с общими настройками пользователя, лимитов и способа связи с веб-сервером.

Типичный конфиг пула /etc/php/8.3/fpm/pool.d/www.conf
[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.

Оценка среднего расхода памяти одним процессом PHP-FPM
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-блок Nginx для сайта на PHP-FPM
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.

Таймаут ожидания ответа от PHP-FPM в конфигурации Nginx
fastcgi_read_timeout 60s;

В самом PHP-FPM за похожее поведение отвечает request_terminate_timeout в конфигурации пула — если он меньше, чем fastcgi_read_timeout в Nginx, PHP-FPM прервёт выполнение скрипта раньше, чем Nginx успеет посчитать это таймаутом на своей стороне, что может приводить к неполным или пустым ответам вместо явной ошибки.

Аналогичный таймаут в конфигурации пула PHP-FPM
request_terminate_timeout = 60s

Практическая рекомендация — держать оба значения синхронизированными и достаточными для самых тяжёлых легитимных операций сайта (например, генерация большого отчёта или импорт каталога), но не бесконечными: без разумного таймаута зависший процесс будет занимать слот в pm.max_children неограниченно долго, лишая ресурсов остальные запросы к тому же самому серверу.

Логи и slow log: как найти конкретную медленную функцию

Обычный error-лог PHP-FPM показывает фатальные ошибки и предупреждения, но не покажет, какая именно строка кода стала причиной, если запрос просто выполняется слишком долго без явной ошибки. Для этого в PHP-FPM есть отдельный механизм — slow log, включаемый двумя параметрами в конфигурации пула.

Включение slow log для запросов дольше 5 секунд
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 для комфортной работы 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 в php.ini или отдельном ini-файле
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.

Чек-лист настройки связки с нуля

Семь шагов настройки Nginx и PHP-FPM: установка, конфигурация пула, server-блок с тем же сокетом, синхронизация таймаутов, лимиты PHP, проверка выполнения кода, slow log и мониторинг

Шаг 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.

Выдаёте ли закрывающие документы?

Да, предоставим необходимые документы для оплаты.

Как оставить заявку?

Заполните форму на странице или нажмите «Заказать» — мы свяжемся и поможем с подбором.

заявка

Оставьте заявку

Ответим и поможем с подбором и настройкой.