# конспект · шаг 1 из 1
Шпаргалка по всему курсу
Весь курс на одной странице: шесть решений, все приоритеты, карта диагностики и готовые блоки конфига.
Шесть решений по каждому запросу
Схема для диагностики, а не точный порядок фаз: return в самом server
срабатывает до выбора location, а смена URI запускает выбор заново - точная
картина в конвейере ниже.
1. какой server? listen (адрес+порт) → server_name → default_server → первый блок
2. какой location? = → самый длинный префикс (с ^~ - стоп) → регулярки → префикс
3. выйти досрочно? return / rewrite (фаза rewrite - ДО access!)
4. кто отдаёт? root/alias + try_files | proxy_pass / fastcgi_pass
5. чем обвесить? add_header, expires, gzip
6. что в лог? access_log (пишется ПОСЛЕ ответа)
Полный конвейер фаз
server rewrite → find location → rewrite → preaccess (limit_*)
→ access (allow/deny, auth) → try_files → content → filters
Отсюда: return в location бьёт deny; return в server{} бьёт выбор
location; лимиты считаются после перезаписи URI.
Приоритеты, которые спрашивают
| Что | Порядок |
|---|---|
server_name |
точное → *.имя → имя.* → регулярка (последняя!) |
location |
= → самый длинный префикс запомнен; если он с ^~ - победил → регулярки по порядку в файле → запомненный префикс |
| наследование | ближайшее значение побеждает; массивы замещаются целиком |
Директивы-массивы (наследуются «всё или ничего»)
add_header, proxy_set_header, fastcgi_param, limit_req, allow/deny.
Одна такая директива в location отменяет все унаследованные.
Готовые блоки
Это фрагменты, а не файл для вставки целиком: у каждого подписан контекст.
upstream, map, зоны лимитов и кеша живут в http, location - внутри
своего server. Имена (shop.local, app-1), пути и сертификаты - стенда,
их заменяют на свои. Блоки ниже прогнаны вместе на nginx 1.31.8.
# ── http: общие объявления ────────────────────────────────────
upstream app { server app-1:8000 max_fails=3 fail_timeout=15s; keepalive 32; }
map $http_upgrade $connection_upgrade { default upgrade; '' close; }
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=API:10m
inactive=60m max_size=2g;
# ── http: catch-all - чужой Host не видит твой сайт ───────────
server { listen 80 default_server; server_name _; return 444; }
# ── http: http → https с исключением для ACME ─────────────────
server {
listen 80;
server_name shop.local www.shop.local; # без имён ответит catch-all
location ^~ /.well-known/acme-challenge/ { root /var/www/certbot; }
location / { return 301 https://$host$request_uri; } # не в server!
}
# ── файл /etc/nginx/snippets/proxy-headers.conf ───────────────
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# ── server: SPA + ассеты ──────────────────────────────────────
location / { try_files $uri $uri/ /index.html; }
location /assets/ {
try_files $uri =404; # честная 404 на отсутствующий файл
# вечный кеш - только файлам с хешем в имени, и без always:
# иначе на год запомнится и 404
add_header Cache-Control "public, max-age=31536000, immutable";
}
# ── server: прокси к группе ───────────────────────────────────
location /api/ {
proxy_pass http://app;
include /etc/nginx/snippets/proxy-headers.conf;
}
# ── server: лимит на вход ─────────────────────────────────────
# точный location не наследует proxy_pass от /api/ - повторяем
location = /api/login {
limit_req zone=login burst=5 nodelay;
limit_req_status 429;
proxy_pass http://app;
include /etc/nginx/snippets/proxy-headers.conf;
}
# ── server: WebSocket ─────────────────────────────────────────
location /ws/ {
proxy_pass http://app;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
}
# ── server: кеш ответов ───────────────────────────────────────
location /api/catalog/ {
proxy_cache API;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 1m;
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
add_header X-Cache-Status $upstream_cache_status always;
proxy_pass http://app;
}
Команды
| Задача | Команда |
|---|---|
| итоговый конфиг | nginx -T |
| проверить и применить | nginx -t && systemctl reload nginx |
| карта чужого сервера | nginx -T \| grep -n "server_name\|listen\|proxy_pass" |
| переоткрыть логи | nginx -s reopen |
| коды ответов за период | awk '{print $9}' access.log \| sort \| uniq -c \| sort -rn |
| проверить цепочку TLS | openssl s_client -connect host:443 -servername host |
| замер отдачи | curl -o /dev/null -s -w 'ttfb=%{time_starttransfer} total=%{time_total}\n' URL |
| состояние сервера | curl -s http://127.0.0.1/nginx_status |
Что делать, если…
Частые причины и с чего начать, а не доказательства: один и тот же симптом дают
разные поломки, и причину называет строка в error_log.
| Ситуация | С чего начать |
|---|---|
| открывается не тот сайт | нет default_server; отвечает первый блок на порту |
| чужой домен показывает твой сертификат | нет catch-all на 443 / ssl_reject_handshake |
| в блок попал не тот запрос | префикс - это строка: /app ловит /application.log |
регулярный server_name не срабатывает |
его перебивает маска |
| 403 на каталоге | нет index и autoindex off |
| «у меня работает, у интеграции нет» | указали cert.pem вместо fullchain.pem |
| пропала строка запроса при редиректе | $uri вместо $request_uri |
| исчезли заголовки безопасности | add_header в location отменил унаследованные |
бэкенд видит Host: app |
нет proxy_set_header Host $host |
502 too big header |
увеличить proxy_buffer_size |
| 504 | этап назван в строке upstream timed out; proxy_read_timeout - пауза между чтениями |
502 no live upstreams |
слишком агрессивные max_fails; при одном сервере - только от down и max_conns |
| 499 | клиент ушёл: смотреть время ответа бэкенда |
| gzip не сжимает | шесть причин: типы, размер, proxied, клиент, уже сжато, 1.0 |
| кеш не работает | нет срока: ни proxy_cache_valid, ни Cache-Control от бэкенда; или ответ с Set-Cookie |
| чужая корзина в кеше | нет bypass/no_cache |
| лимит отдаёт 503 | limit_req_status 429 |
| за прокси все клиенты - один | нужен realip |
| продление сертификата сломалось | return 301 стоит прямо в server, а не в location / |
| HTTP/3 не включается | UDP/443 закрыт или нет Alt-Svc |
| SPA отдаёт 200 на отсутствующий ассет | ассетам нужен свой location с =404 |
| после reload висят воркеры | worker_shutdown_timeout |
| логи пишутся «в никуда» после ротации | nginx -s reopen |
| reload вернул ноль, а конфиг не применился | занят порт или нет файла - смотреть error_log, код возврата врёт |
| проксируемый запрос отдаёт 500 | часто кончились слоты (в worker_connections входят и соединения к бэкендам) - ищи [alert] в error_log |
| белая страница при 200 в логах | нет include mime.types, а nosniff запрещает исполнять text/plain |
| 404 ассета закешировался на год | always на вечном кеше уводит заголовок и на ошибку |
SMTP (25, 587) или IMAP (143) за stream не работает |
там первым говорит сервер, а ssl_preread ждёт клиента: 30 секунд и обрыв; на 465 и 993 TLS сразу, и ssl_preread работает |
| forward proxy отвечает 502 на всё | не задан resolver |
Свежее, чего нет в старых статьях
http2 on;директивой (1.25.1), а не флагом вlisten;proxy_http_versionпо умолчанию 1.1 иkeepaliveвключён сам (1.29.7);least_timeоткрыт для всех (1.31.0),stickyсroute/drain(1.29.6);ssl_staplingс сертификатами Let's Encrypt - мёртвая строка (OCSP выключен в 2025);- server push мёртв, вместо него
early_hints on;(1.29.0); add_header_inherit(1.29.3),max_headers(1.29.8);- нативный ACME-модуль (1.29.0), forward proxy
tunnel_pass(1.31.0); - виртуальные серверы и
passвstream- с 1.25.5; json_set- разбор JSON прямо в конфиге, путь пишется точками;- njs 1.0 объявил старый движок устаревшим, но пакет под 1.31.5 несёт именно его;
- brotli и zstd не входят в стандартную сборку.
Куда идти дальше
Своя песочница в контейнере и повторить разбор поломок руками: снять default_server,
перепутать слеши в proxy_pass, поставить add_header в location, включить лимит
1r/s - и посмотреть на симптом своими глазами.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий