FastCGI и PHP: nginx + PHP-FPM (и uwsgi/scgi)

Рейтинг: 67.6% · 8 голосов
Самый подробный курс по Nginx - от первого конфига до production API-gateway. Архитектура и event loop, контексты и location, виртуальные хосты, статика, reverse proxy и upstream-балансировка, кэширование, HTTPS и TLS (post-quantum), HTTP/2 и HTTP/3 QUIC, безопасность и rate limiting, realip, JSON-логи и сквозная трассировка, метрики, тюнинг и траблшутинг. Плюс OpenResty (Lua, фазы, JWT, динамика) и Angie (нативные Prometheus, ACME, dynamic upstream). На основе эталонного gateway и лучших практик. Для новичков и SRE/DevOps. Актуально на 2026 (nginx 1.30/1.31, Angie, ingress-nginx EOL -> Gateway API).
Ответить
Аватара пользователя
Igor_NgINX
Сообщения: 44
Зарегистрирован: 11 май 2026, 05:31

FastCGI и PHP: nginx + PHP-FPM (и uwsgi/scgi)

Сообщение Igor_NgINX »

Оглавление курса (44)
  1. Что такое Nginx и почему он захватил веб
  2. Установка Nginx, Angie и OpenResty: пакеты, версии, Docker
  3. Первый конфиг с нуля: минимальный сервер за 5 минут
  4. Архитектура процессов: master, worker и event loop
  5. Анатомия конфигурации: контексты, наследование и модульность
  6. Виртуальные хосты: server и server_name
  7. Директива location: матчинг и приоритеты по шагам
  8. Раздача статики: root, alias, try_files, sendfile
  9. Переменные, map и блок if
  10. rewrite, return и канонизация URL
  11. Работа с заголовками: add_header и proxy_set_header
  12. Модель доверия прокси-заголовков: realip и защита от спуфинга
  13. Сжатие: gzip, brotli и zstd
  14. Reverse proxy: proxy_pass и проброс запроса
  15. upstream и балансировка нагрузки
  16. Таймауты, повторы и устойчивость прокси
  17. FastCGI и PHP: nginx + PHP-FPM (и uwsgi/scgi) (вы здесь)
  18. WebSocket, gRPC и стриминг через Nginx
  19. Кэширование ответов: proxy_cache и микрокэш
  20. HTTPS и TLS: сертификаты, протоколы, шифры (2026)
  21. Let's Encrypt и ACME: certbot, acme.sh, нативный ACME Angie
  22. HTTP/2 и HTTP/3 (QUIC) в 2026
  23. Безопасность: заголовки, скрытие версии, ограничения
  24. Rate limiting и защита от перегрузки
  25. Контроль доступа: allow/deny, auth_basic, auth_request и mTLS
  26. Логирование: access_log, форматы и структурированный JSON
  27. Сквозная трассировка запросов и OpenTelemetry
  28. Метрики и мониторинг Nginx
  29. Производительность и тюнинг под нагрузку
  30. Траблшутинг: 502, 504, 403, 404 и debug-лог
  31. OpenResty: Nginx как платформа на LuaJIT
  32. Фазы обработки запроса и Lua-хуки
  33. Lua API: ngx.*, shared dict, cosocket и lua-resty-core
  34. Практика OpenResty: авторизация, JWT, кэш, Redis
  35. Angie: современный форк Nginx 2026
  36. Динамическая конфигурация и service discovery
  37. Nginx как API-gateway и Kubernetes Gateway API
  38. Динамические модули, njs и WAF
  39. Stream-модуль: проксирование TCP и UDP
  40. Деплой, перезагрузка и конфиг как код
  41. Сценарий: статика, SPA и кэширование за CDN
  42. Сценарий: микросервисный gateway по образцу ngx-trace-gateway
  43. Капстоун: собираем production-gateway с нуля
  44. Чек-лист безопасности, CVE 2026 и аудит конфигурации
До этого момента nginx у нас был отдатчиком статики и обратным прокси к HTTP-бэкендам. Но добрая половина интернета - это PHP: WordPress, Laravel, Битрикс, старые самописы. PHP по HTTP сам не разговаривает. И вот тут возникает вопрос, на котором новички спотыкаются годами: а как именно nginx отдаёт PHP? Ответ - через протокол FastCGI и демон PHP-FPM. Разберёмся в механике до винтика: как устроена связка nginx php, где живёт классическая ошибка с путём к скрипту, почему без try_files твой сервер выполнит чужой загруженный файл, и как ускорить динамику кэшем.

Почему FastCGI, а не proxy_pass: механика nginx fastcgi

Когда ты делаешь proxy_pass на HTTP-апстрим, nginx говорит с бэкендом на HTTP. PHP-FPM (FastCGI Process Manager) HTTP не понимает - он понимает бинарный протокол FastCGI. Это не каприз, а оптимизация: HTTP-запрос - это текст, который надо парсить. FastCGI же передаёт уже разобранный запрос как набор пар ключ-значение (CGI-переменные: REQUEST_METHOD, QUERY_STRING, SCRIPT_FILENAME и десятки других) плюс тело. PHP-FPM получает готовые переменные окружения и сразу зовёт интерпретатор - парсить HTTP-заголовки ему не нужно.

Архитектурно это выглядит так. PHP-FPM - это master-процесс, который держит пул воркеров (готовых процессов PHP). Запрос от nginx прилетает по FastCGI, свободный воркер его берёт, исполняет скрипт, отдаёт результат и освобождается под следующий. Воркеры не умирают после каждого запроса (в отличие от древнего CGI), отсюда и буква F - Fast. nginx тут чистый клиент FastCGI: он формирует переменные, открывает соединение к php-fpm, шлёт запрос, читает ответ и отдаёт его браузеру.

Соединяться можно двумя способами, и это первый практический выбор. Через unix-сокет:

Код: Выделить всё

fastcgi_pass unix:/run/php/php8.3-fpm.sock;
или через TCP:

Код: Выделить всё

fastcgi_pass 127.0.0.1:9000;
Если nginx и PHP-FPM на одной машине - бери unix-сокет. Он быстрее (нет TCP-оверхеда, нет трёхстороннего рукопожатия, нет TIME_WAIT-сокетов под нагрузкой) и его не видно снаружи. TCP нужен, когда PHP-FPM на другом хосте или в отдельном контейнере (в Docker-сетях это обычная история - там пишут fastcgi_pass php:9000 по имени сервиса). Грабля: при unix-сокете права на файл сокета должны позволять пользователю nginx в него писать - смотри listen.owner/listen.group/listen.mode в пуле PHP-FPM, иначе получишь 502 и connect() failed (13: Permission denied) в логе.

Изображение

Минимальный рабочий location для nginx php-fpm

Соберём базовую конфигурацию и разберём каждую строку. Это тот самый location, который ты будешь писать в каждом PHP-vhost:

Код: Выделить всё

server {
    listen 80;
    server_name example.com;
    root /var/www/example/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_index index.php;
        fastcgi_read_timeout 60s;
    }
}
Что тут происходит по шагам. Браузер просит /blog/. location / ловит запрос, try_files ищет файл /blog, потом каталог /blog/, не находит и по последнему аргументу отдаёт управление на /index.php?$query_string - это фронт-контроллер, единая точка входа Laravel и WordPress. Дальше URI вида /index.php матчится регуляркой location ~ \.php$, и начинается FastCGI.

include fastcgi_params подтягивает стандартный набор CGI-переменных (он лежит в дистрибутиве рядом с nginx.conf). Там уже прописаны REQUEST_METHOD, QUERY_STRING, REMOTE_ADDR, HTTP-заголовки как HTTP_* и прочее. Чего там обычно нет или что задано неполно - это SCRIPT_FILENAME, поэтому его дописывают отдельно. fastcgi_index index.php подставляет имя индексного скрипта, если URI заканчивается слешем и ушёл в FastCGI.

Главная грабля: SCRIPT_FILENAME и путь к скрипту

Вот строка, из-за которой пролито море крови:

Код: Выделить всё

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
SCRIPT_FILENAME - это абсолютный путь к PHP-файлу, который PHP-FPM откроет и выполнит. nginx сам файл не читает - он только говорит PHP-FPM "выполни вот этот путь". И если путь неверный, ты получишь пустой белый экран или ошибку File not found. в браузере (это не 404 от nginx, это ответ самого PHP-FPM - важное отличие при диагностике).

Разберём переменные. $document_root - это значение директивы root, например /var/www/example/public. $fastcgi_script_name - имя скрипта из URI, например /index.php. Склеиваем - получаем /var/www/example/public/index.php. Правильно.

Где ломается. Классика номер один: люди прописывают root внутри location ~ \.php$ заново, причём с другим значением, и пути расходятся. Или пишут хардкод вроде fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name, а реальный root другой. Тогда PHP-FPM ищет файл не там. Правило простое: root задавай ОДИН раз на уровне server, а SCRIPT_FILENAME собирай через $document_root, чтобы он автоматически совпадал с root. Именно так сделано в эталонном проекте ngx-trace-gateway в global_fastcgi.conf:

Код: Выделить всё

include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME     $fastcgi_script_name;
Грабля номер два - PATH_INFO. Старые приложения использовали URL вида /index.php/article/42, где /article/42 - это PATH_INFO, дополнительный путь после имени скрипта. Чтобы nginx правильно разделил имя скрипта и path info, применяют fastcgi_split_path_info:

Код: Выделить всё

location ~ ^(.+\.php)(/.+)$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    try_files $fastcgi_script_name =404;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
Регулярка делит URI на группу 1 (до .php включительно) и группу 2 (хвост). $fastcgi_script_name становится первой группой, $fastcgi_path_info - второй. Большинству современных приложений (Laravel, чистый WordPress) PATH_INFO не нужен - они роутят через query string. Не тащи split_path_info, если он тебе не нужен: он расширяет поверхность атаки, о которой ниже.

try_files перед FastCGI - это вопрос безопасности, а не удобства

Теперь самое важное в уроке. Строка try_files $uri =404; (или try_files $fastcgi_script_name =404;) внутри php-location - не косметика. Это защита от выполнения произвольных файлов, и без неё твой сервер можно взломать.

Механика дыры. Исторически в PHP была опция cgi.fix_pathinfo=1 (когда-то она шла по умолчанию). При ней, если PHP-FPM получает SCRIPT_FILENAME на несуществующий файл с хвостом, он отрезает хвост и пытается выполнить ближайший существующий файл. Сценарий атаки: пользователь загрузил на сайт картинку avatar.jpg (легально, аватарки разрешены). Затем запрашивает /uploads/avatar.jpg/x.php. Если nginx тупо матчит .php в конце URI и шлёт это в PHP-FPM без проверки существования, PHP с fix_pathinfo находит реальный avatar.jpg и ВЫПОЛНЯЕТ его как PHP-код. А внутри картинки спрятан PHP-шелл. Это классическая удалённая компрометация серверов на nginx, которой много лет.

try_files $uri =404; разрывает эту цепочку: nginx проверяет, что файл /uploads/avatar.jpg/x.php реально существует на диске. Не существует - сразу 404, до всякого FastCGI. PHP-FPM запрос вообще не видит. Дополнительно в современных PHP-FPM по умолчанию security.limit_extensions = .php, что отсекает попытку выполнить .jpg. Но полагаться на один слой нельзя - держи оба: try_files на стороне nginx плюс security.limit_extensions на стороне PHP-FPM. Глубокая защита.

Второй слой защиты на уровне путей - запретить выполнение PHP в каталогах загрузок прямо в конфиге. Эталонный ngx-trace-gateway делает это в app_redirects_security.conf:

Код: Выделить всё

# Запрет выполнения .php в /upload - тут только файлы данных.
location ~* ^/upload/.*\.php$ {
    deny all;
}

location ~* ^/(cgi-bin|includes|uploads|tmp|php-cgi)/ {
    deny all;
}
Логика: в каталог загрузок попадает пользовательский контент, значит туда не должна попадать исполнимая логика. Даже если кто-то загрузит туда .php - nginx отдаст 403 ещё до FastCGI. Помни про приоритет location: регулярочные location проверяются в порядке записи, поэтому deny-блок для /upload должен стоять РАНЬШЕ общего location ~ \.php$, иначе общий перехватит запрос первым. Альтернатива без гонки приоритетов - использовать location ^~ /upload/ с префиксным матчингом, который имеет приоритет над регулярками.

Буферы, таймауты и пулы: эксплуатация под нагрузкой

PHP бывает медленным: тяжёлый импорт, генерация отчёта, выгрузка. По умолчанию fastcgi_read_timeout 60 секунд - время ожидания ответа от php-fpm после отправки запроса. Если скрипт считает дольше, nginx рвёт соединение и отдаёт 504 Gateway Time-out. Поднимать вслепую до 600s - плохая идея: повисший воркер держит соединение и занимает слот в пуле. Лучше для конкретного тяжёлого эндпоинта поднять таймаут точечно:

Код: Выделить всё

location = /admin/export.php {
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_read_timeout 300s;
}
И параллельно подними max_execution_time в самом PHP, иначе PHP сам прибьёт скрипт раньше nginx.

Буферы. Когда PHP отдаёт большой ответ (длинная HTML-страница, JSON-портянка), nginx буферизует его, чтобы быстро освободить воркер php-fpm и не держать его, пока медленный клиент по мобильной сети тянет ответ байт за байтом. Эталонные значения из global_fastcgi.conf - разумная база для веб-приложений:

Код: Выделить всё

fastcgi_connect_timeout 5s;
fastcgi_read_timeout 10s;
fastcgi_send_timeout 10s;
fastcgi_buffering on;
fastcgi_buffer_size 16k;
fastcgi_buffers 16 16k;
fastcgi_busy_buffers_size 32k;
fastcgi_temp_path /var/run/openresty/nginx-fastcgi;
fastcgi_next_upstream error timeout;
fastcgi_next_upstream_tries 3;
fastcgi_buffer_size - первый буфер под заголовки ответа. fastcgi_buffers 16 16k - 16 буферов по 16k под тело. Если ответ не влез в память - излишек пишется во временный файл (fastcgi_temp_path), это медленнее, поэтому буферы подбирают под типичный размер ответа. Грабля: при стриминге (Server-Sent Events, длинные опросы) буферизация всё ломает - клиент не видит данные, пока php-fpm не закроет ответ. Для таких location ставь fastcgi_buffering off; точечно.

Со стороны PHP-FPM крутится пул процессов: pm = dynamic/static/ondemand, pm.max_children. Это потолок одновременных PHP-запросов. Если все воркеры заняты, новые запросы встают в очередь backlog сокета, а при переполнении nginx ловит 502. Правило прикидки: pm.max_children = (доступная память) / (память на один воркер). Перебор - сервер уйдёт в своп и ляжет; недобор - очередь и 502 под пиком. nginx это не лечит - тут тюнят PHP-FPM.

nginx fastcgi_cache: ускоряем PHP кэшированием ответа

Самый дешёвый способ ускорить PHP-сайт - не выполнять PHP. Если страница для анонима одинаковая (главная блога, карточка товара без персонализации), nginx может закэшировать готовый HTML и отдавать его напрямую с диска, минуя php-fpm. Это nginx fastcgi_cache, и на типичном WordPress он даёт кратный рост RPS - с десятков до тысяч запросов в секунду.

Сначала объявляем зону кэша на уровне http (по аналогии с proxy_cache_path в эталонном global_cache.conf):

Код: Выделить всё

fastcgi_cache_path /var/cache/nginx/fcgi levels=1:2
    keys_zone=phpcache:64m inactive=60m max_size=4g use_temp_path=off;
keys_zone=phpcache:64m - shared memory под индекс ключей (не под сами данные, данные на диске). levels=1:2 раскидывает файлы по подкаталогам, чтобы не упереться в лимиты ФС. inactive=60m выкидывает невостребованное, max_size=4g - потолок на диске.

Теперь в php-location включаем кэш. Эталон формирует ключ и политику так:

Код: Выделить всё

location ~ \.php$ {
    try_files $uri =404;
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    fastcgi_cache phpcache;
    fastcgi_cache_key "$scheme$request_method$host$request_uri$http_authorization";
    fastcgi_cache_valid 200 1h;
    fastcgi_cache_lock on;
    fastcgi_cache_lock_timeout 5s;
    fastcgi_cache_background_update on;
    fastcgi_cache_revalidate on;
    fastcgi_ignore_headers Expires Cache-Control Set-Cookie Vary;
    fastcgi_hide_header Set-Cookie;
    add_header X-FastCGI-Cache $upstream_cache_status always;
}
Разбор по строкам. fastcgi_cache_key собирает уникальный ключ из схемы, метода, хоста, URI и заголовка Authorization - последнее, чтобы авторизованные пользователи не получили чужой кэш. fastcgi_cache_valid 200 1h кэширует только успешные ответы на час. fastcgi_cache_lock on - защита от dog-pile: когда кэш протух и сто запросов прилетели разом, в php-fpm уйдёт только ОДИН, остальные подождут результата (до lock_timeout), вместо того чтобы стадом убить бэкенд. fastcgi_cache_background_update on отдаёт протухший ответ мгновенно, а свежий тянет в фоне - клиент не ждёт. add_header X-FastCGI-Cache $upstream_cache_status always добавляет в ответ диагностический заголовок: HIT, MISS, BYPASS, EXPIRED, STALE - бесценно при отладке (curl -I и смотришь, попал ли в кэш).

Критическая тонкость: НЕЛЬЗЯ кэшировать авторизованных и POST. Иначе залогиненный юзер увидит кэш гостя или чужой. Управляют этим парой fastcgi_cache_bypass (не отдавать ИЗ кэша) и fastcgi_no_cache (не класть В кэш) через map:

Код: Выделить всё

map $request_method $no_cache_method { default 0; POST 1; }
map $http_cookie $no_cache_cookie {
    default 0;
    "~*wordpress_logged_in|woocommerce_items_in_cart|PHPSESSID" 1;
}

# внутри location ~ \.php$:
set $skip_cache 0;
if ($no_cache_method) { set $skip_cache 1; }
if ($no_cache_cookie) { set $skip_cache 1; }
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
Если есть кука логина или метод POST - $skip_cache=1, запрос идёт мимо кэша в обе стороны. Это рабочий паттерн для nginx wordpress: гости получают молниеносный кэш, залогиненные - живой PHP, корзина и админка не ломаются.

Симметрия политик: fastcgi, uwsgi, scgi - зеркало proxy

FastCGI - не единственный протокол приложений. Python-приложения (Flask, Django через uWSGI) общаются по протоколу uwsgi - и у nginx для него СВОЙ зеркальный набор директив: uwsgi_pass, uwsgi_read_timeout, uwsgi_buffers, uwsgi_cache, uwsgi_next_upstream. То же для протокола SCGI: scgi_pass и компания. Это ключевая идея архитектуры nginx - каждый протокол апстрима (proxy/fastcgi/uwsgi/scgi) имеет ОДИНАКОВЫЙ по смыслу набор директив с разным префиксом.

Эталонный проект использует это буквально: global_uwsgi.conf и global_scgi.conf - почти посимвольная копия global_fastcgi.conf, только с заменой префикса. Те же таймауты 5s/10s/10s, те же буферы 16 на 16k, тот же next_upstream tries 3, та же политика кэша и тот же ключ:

Код: Выделить всё

# global_uwsgi.conf (зеркало fastcgi)
uwsgi_connect_timeout 5s;
uwsgi_read_timeout 10s;
uwsgi_buffer_size 16k;
uwsgi_buffers 16 16k;
uwsgi_next_upstream error timeout;
uwsgi_next_upstream_tries 3;
include uwsgi_params;
uwsgi_cache_key "$scheme$request_method$host$request_uri$http_authorization";
Python-приложение подключается так:

Код: Выделить всё

location / {
    include uwsgi_params;
    uwsgi_pass unix:/run/uwsgi/app.sock;
}
Практическая ценность симметрии: освоив тюнинг одного протокола, ты автоматически знаешь все четыре. Логика буферов, таймаутов, failover и кэша одинакова - меняется только префикс. Поэтому держи политики в отдельных include-файлах (как эталон), а в vhost оставляй только pass и специфику. Меняешь таймаут глобально в одном месте - он применяется ко всем хостам. uwsgi сегодня нишевый (молодые Python-проекты чаще ходят через ASGI/HTTP за proxy_pass), а scgi - совсем редкий гость, но знать про симметрию обязан: встретишь старый стек - сразу поймёшь, как он устроен.

Типичные грабли FastCGI
  • 502 Bad Gateway + connect() failed Permission denied - неверные права на unix-сокет php-fpm. Чини listen.owner/listen.group/listen.mode в пуле.
  • File not found. в браузере (не 404 от nginx) - неверный SCRIPT_FILENAME. Проверь, что root задан и $document_root совпадает с реальным путём. Это ответ php-fpm, а не nginx.
  • Белый экран без ошибок - PHP упал с фаталом, но display_errors=off. Смотри php-fpm error log и логи приложения, а не nginx.
  • Дыра RCE через загрузки - забыли try_files перед FastCGI или не запретили .php в /upload. Всегда оба слоя.
  • Кэш отдаёт залогиненным гостевую страницу - не настроены fastcgi_cache_bypass/no_cache по кукам. Проверяй X-FastCGI-Cache в ответе.
  • 504 Gateway Time-out на тяжёлом скрипте - мал fastcgi_read_timeout И/ИЛИ max_execution_time. Поднимай ОБА, точечно на location.
  • Расходящийся root в location ~ \.php$ - не переопределяй root внутри php-location. Один root на server.
  • Кэш не обновляется после деплоя - не забудь чистку: удалить файлы из fastcgi_cache_path или продумать инвалидацию (в open-source nginx нет встроенной очистки по ключу - это purge есть в коммерческом Plus и в Angie).
Мини-лаба: подними WordPress-стиль конфиг руками

Повтори по шагам на тестовой машине или в контейнере.
  • Шаг 1. Установи php-fpm (apt install php8.3-fpm) и проверь сокет: ls -la /run/php/php8.3-fpm.sock.
  • Шаг 2. Создай /var/www/test/public и положи туда info.php с содержимым вызова phpinfo(). Сделай root каталог доступным nginx.
  • Шаг 3. Напиши server с root /var/www/test/public, location / с try_files $uri $uri/ /index.php?$query_string и location ~ \.php$ с try_files $uri =404, include fastcgi_params, fastcgi_pass на сокет и SCRIPT_FILENAME через $document_root.
  • Шаг 4. nginx -t && nginx -s reload. Открой /info.php - увидишь страницу phpinfo. Открой несуществующий /nope.php - должен прийти 404 от nginx, НЕ File not found от php-fpm. Если пришёл File not found - у тебя нет try_files, дыра открыта.
  • Шаг 5. Положи в /var/www/test/public/uploads/shell.jpg текст с PHP-кодом. Запроси /uploads/shell.jpg/x.php. Без защиты возможно выполнение. Добавь location ~* ^/uploads/.*\.php$ { deny all; } ВЫШЕ php-location и повтори - получи 403.
  • Шаг 6. Включи fastcgi_cache: объяви fastcgi_cache_path в http, добавь fastcgi_cache, ключ, valid 200 1h и add_header X-FastCGI-Cache $upstream_cache_status always. Дёрни curl -I http://localhost/ дважды - второй раз заголовок должен показать HIT.
Контрольные вопросы
  • Почему try_files перед fastcgi_pass - это про безопасность, а не про удобство? Какую атаку он закрывает?
  • Из чего собирается SCRIPT_FILENAME и почему нельзя переопределять root внутри location ~ \.php$?
  • Чем отличаются fastcgi_cache_bypass и fastcgi_no_cache, и почему для nginx wordpress нужны оба?
  • В чём смысл симметрии fastcgi/uwsgi/scgi с proxy и как это упрощает эксплуатацию?
Итог

nginx отдаёт PHP не по HTTP, а по FastCGI в php-fpm - через unix-сокет (быстрее, локально) или TCP (по сети/в контейнерах). Сердце настройки - правильный SCRIPT_FILENAME через $document_root$fastcgi_script_name и единственный root на server. try_files перед FastCGI и запрет .php в загрузках - это не удобство, а защита от выполнения чужого кода, держи оба слоя. fastcgi_cache даёт кратное ускорение, но требует аккуратного bypass/no_cache для авторизованных и POST. А вся связка fastcgi/uwsgi/scgi - зеркало proxy с одинаковой логикой буферов, таймаутов и кэша: понял один протокол - понял все четыре.
👍4 ❤️1 🔥2 😄 🤔2
Аватара пользователя
grpc_geek
Сообщения: 1
Зарегистрирован: 08 июн 2026, 12:02

Re: FastCGI и PHP: nginx + PHP-FPM (и uwsgi/scgi)

Сообщение grpc_geek »

Спасибо, наконец дошло про try_files $uri =404 - я думал это просто чтобы красивая 404 была, а оно реально дыру закрывает. Пошел перепроверять свой старый vhost с аватарками, там как раз не было такой строки. Жесть.
👍1 ❤️1 🔥2 😄 🤔
Аватара пользователя
coldburnout
Сообщения: 1
Зарегистрирован: 26 май 2026, 03:59

Re: FastCGI и PHP: nginx + PHP-FPM (и uwsgi/scgi)

Сообщение coldburnout »

А есть разница для fastcgi_pass между unix:/run/php/php8.3-fpm.sock и 127.0.0.1:9000 если все на одной машине? У меня в докере php отдельным контейнером, я так понял мне только tcp вариант доступен через имя сервиса?
👍2 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Таймауты, повторы и устойчивость прокси
Следующая глава →
WebSocket, gRPC и стриминг через Nginx

Все главы курса «Nginx профессионально: от первого конфига до API-gateway на OpenResty и Angie»

Поделиться темой: ✈ Telegram VK
Похожие запросы: nginx и PHP-FPM: настройка FastCGI

Вернуться в «Nginx профессионально: от первого конфига до API-gateway на OpenResty и Angie»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя