⚙️ Теория
🔄 Основные фазы
В упрощённой модели HTTP-запрос проходит через ключевые фазы:
rewriteaccesscontentlog
Что делает каждая:
rewrite
меняет URI, аргументы, выполняетrewrite,return,set,if(в рамках rewrite-модуля).access
проверяет доступ: ACL, auth, limit_req/limit_conn и другие access-проверки.content
формирует ответ:proxy_pass,fastcgi_pass,uwsgi_pass,return, static file handler.log
финальная фаза записи логов после завершения обработки запроса.
🧠 Phase engine = state machine
phase engine в NGINX — это конечный автомат, который двигает request по цепочке хендлеров.
Ключевые коды возврата:
NGX_OK— переход к следующей фазе;NGX_DECLINED— переход к следующему handler в текущей фазе;NGX_AGAIN/NGX_DONE— временная пауза обработки (возврат в event loop), продолжение позже.
🔁 Internal redirect перезапускает фазы
Когда происходит internal redirect (например rewrite ... last, try_files, error_page), меняется URI/локация.
После этого NGINX заново делает location lookup и повторно запускает фазовый проход уже для нового URI.
Отдельно: в rewrite-модуле такой цикл ограничен 10 итерациями, после чего возвращается 500.
⏳ NGX_AGAIN возвращает управление в event loop
NGX_AGAIN означает: операция ещё не завершена, нужно подождать событие I/O.
В этот момент request не блокирует worker: управление уходит в event loop, а обработка продолжится позже при новом событии.
❓ Что нужно уметь объяснить
Почему redirect требует нового location lookup?
Потому что после redirect у запроса уже другой URI.
Старый location может больше не подходить, поэтому NGINX обязан заново выбрать самый подходящий location и только потом продолжить фазы.
Что произойдет, если handler вернет NGX_DECLINED?
Это не ошибка и не финальный ответ.
Смысл: «этот handler не обработал запрос». Тогда phase engine передаст request следующему handler в текущей фазе (или пойдёт дальше по фазам, если это ожидаемое поведение для конкретной фазы).
Чем rewrite отличается от content handler?
rewriteменяет маршрут запроса (URI/правила), но обычно не генерирует тело ответа;content handler— это место, где реально формируется ответ клиенту (файл, proxy, fastcgi, return и т.д.).
Коротко: rewrite решает куда идти, content решает что отдавать.
🧪 Практика
1. Конфиг с rewrite + proxy_pass
server {
listen 8080;
server_name _;
location /api/ {
rewrite ^/api/v1/(.*)$ /backend/$1 break;
proxy_pass http://127.0.0.1:18080;
}
}
Здесь rewrite ... break меняет URI внутри текущего location, после чего запрос уходит в proxy_pass.
2. Сделать internal redirect
server {
listen 8080;
server_name _;
location /old/ {
rewrite ^/old/(.*)$ /new/$1 last;
}
location /new/ {
proxy_pass http://127.0.0.1:18080;
}
}
Ключевой момент: last вызывает internal redirect, поэтому NGINX заново делает location lookup для /new/....
3. Включить и посмотреть debug log
В nginx.conf:
error_log /var/log/nginx/error.log debug;
Для debug-уровня NGINX должен быть собран с опцией --with-debug.
Проверка и reload:
nginx -t && sudo nginx -s reload
Смотреть фазовый проход в логе:
sudo tail -f /var/log/nginx/error.log
И запускать запросы параллельно:
curl -i http://127.0.0.1:8080/api/v1/test
curl -i http://127.0.0.1:8080/old/test
🧾 Вывод
Фазы NGINX удобнее понимать как state machine: каждый хендлер либо продолжает маршрут запроса, либо завершает его, либо откладывает до следующего I/O события.
rewrite и internal redirect влияют на маршрут и могут перезапускать фазовый проход, а content формирует итоговый ответ.
Для диагностики спорных кейсов лучший инструмент — debug-лог, где видно движение запроса по фазам и возвратные коды хендлеров.
📚 Ссылки
- NGINX docs: How nginx processes a request
- NGINX docs: rewrite module
- NGINX docs: location priority
- NGINX Development guide: Phases and handlers