Nginx — конфиги под задачу
Для кого эта статья
Если в поиске «nginx конфиг для статики», «nginx reverse proxy node», «nginx react try_files», «nginx php-fpm» или «nginx ssl letsencrypt» — здесь готовые фрагменты с построчным разбором: что означает каждая строка, что увидит браузер и как проверить из терминала.
Статья рассчитана на:
- школьников и студентов на первых лабораторных по администрированию, сетям и DevOps;
- начинающих разработчиков, которые подняли сайт на VPS и ищут рабочий
nginx.conf; - тех, кто нашёл чужой конфиг и хочет понять, зачем там
try_files,proxy_passи слэш в конце URL.
Полный справочник директив — Nginx. Как веб-сервер вписан в цепочку «DNS → HTTP → приложение» — веб-серверы. Nginx в Docker — готовые стеки Compose. Проверка ответов — curl.
Nginx (читают «энджинкс») — программа на сервере, которая принимает HTTP-запросы от браузера и либо отдаёт файл с диска, либо передаёт запрос дальше (в Node.js, Python, PHP-FPM). Настройки лежат в текстовых файлах *.conf; после правки обязательно nginx -t, затем reload.
Как читать конфиг по частям
Nginx читает конфиг сверху вниз. Директива заканчивается ;, блок — { }. Запрос попадает в один блок server (по порту и домену), затем nginx выбирает один location (по URL).
Блоки (контексты)
| Блок | Зачем нужен |
|---|---|
events { } | Сколько соединений держит один рабочий процесс |
http { } | Всё про HTTP — MIME, gzip, upstream, include сайтов |
server { } | Один «виртуальный хост» — домен + порт + корень сайта |
location /path { } | Правило для URL, начинающегося с /path |
upstream имя { } | Список бэкендов для балансировки и proxy |
Частые директивы
| Директива | Смысл простыми словами |
|---|---|
listen 80 | Слушать порт 80 (обычный HTTP) |
listen 443 ssl | Слушать HTTPS |
server_name example.com | Этот блок срабатывает, если в запросе Host: example.com |
root /var/www/site | Папка на диске: URL /img/a.png → файл /var/www/site/img/a.png |
index index.html | Если URL — каталог, отдать этот файл |
try_files $uri … | Порядок «найти файл на диске или сделать fallback» |
proxy_pass http://… | Не искать файл — отправить запрос другому серверу |
return 301 URL | Сразу ответ «перейди по другому адресу» |
include файл | Подключить ещё один конфиг |
Переменные — «подстановки» в конфиге
| Переменная | Откуда берётся | Пример значения |
|---|---|---|
$uri | Путь запроса без query | /api/users |
$request_uri | Путь и query | /api/users?page=1 |
$host | Заголовок Host | shop.example.com |
$remote_addr | IP клиента | 203.0.113.5 |
$scheme | http или https | https |
$document_root | Значение root | /var/www/site |
Два адреса для proxy_pass — самая частая путаница на лабораторных:
| Ситуация | Пишите в proxy_pass |
|---|---|
| Nginx и app на одной Linux-машине | http://127.0.0.1:3000 |
| Nginx в Docker, app — другой контейнер | http://api:3000 (имя сервиса из compose) |
127.0.0.1 внутри контейнера nginx — это сам контейнер, не ваш ПК и не соседний сервис.
Как работать с примерами
- Конфиги на Ubuntu/Debian —
/etc/nginx/, главныйnginx.conf, сайты вconf.d/*.confилиsites-enabled/. - Сохраните фрагмент, например
/etc/nginx/conf.d/my-site.conf. - Проверка и перезагрузка:
sudo nginx -t
sudo systemctl reload nginx
- Смотрите ответ:
curl -I http://localhost
curl -I -H "Host: shop.local" http://127.0.0.1
- Логи: ошибки —
/var/log/nginx/error.log, все запросы —access.log.
Правка /etc/nginx/ — через sudo. Ключ TLS (ssl_certificate_key) не кладите в Git. Домены и пути в примерах замените на свои.
Обязательный каркас
Перед любым примером ниже — минимальный nginx.conf (или один файл в conf.d/, если в главном уже есть include /etc/nginx/conf.d/*.conf;):
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# server { } из примеров — сюда или в conf.d/
}
| Строка | Что делает |
|---|---|
events { worker_connections 1024; } | Без блока events nginx не запустится |
include mime.types | Сопоставляет .css → text/css, .js → application/javascript |
default_type application/octet-stream | Если расширение неизвестно — отдать как «сырые байты» |
sendfile on | Ядро ОС отдаёт файл с диска быстрее, без лишнего копирования в память |
keepalive_timeout 65 | Держать HTTP-соединение открытым до 65 с (меньше рукопожатий TCP) |
Что делает команда проверки:
sudo nginx -t
- Nginx читает все
includeи склеивает конфиг в памяти. - Проверяет синтаксис (скобки, точки с запятой, директивы в правильном блоке).
- Печатает
syntax is okиtest is successful— только после этого безопасенreload.
Стартовые конфиги
Пять сценариев, которые чаще всего ищут на первых курсах — от HTML на диске до прокси на backend.
1. Статический сайт — «Hello»
Задача: отдать готовый index.html с диска. Подходит для лабораторной «поднять веб-сервер», для простой визитки или папки dist/ после сборки фронта без client-side роутинга.
Когда искать этот конфиг: «nginx отдача статики», «nginx index.html», «nginx root try_files».
server {
listen 80;
server_name localhost;
root /var/www/hello;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
| Строка | Смысл |
|---|---|
server { … } | Один виртуальный хост — набор правил для домена |
listen 80 | Принимать HTTP на порту 80 |
server_name localhost | Блок выбирается, если браузер шлёт Host: localhost |
root /var/www/hello | URL /logo.png → файл /var/www/hello/logo.png |
index index.html | Запрос / или /about/ → попробовать index.html в каталоге |
location / | Правило для любого пути (префикс /) |
try_files $uri $uri/ =404 | 1) файл как есть 2) каталог + index 3) иначе ошибка 404 |
Что происходит по шагам (запрос GET /):
- Nginx выбирает этот
server(порт 80,Host: localhost). - Путь
/попадает вlocation /. try_files: ищет файл/— нет; ищет каталог/var/www/hello/— есть.- Из-за
index index.htmlотдаёт/var/www/hello/index.htmlсо статусом 200.
Что происходит (запрос GET /missing.txt):
- Файла
/var/www/hello/missing.txtнет, каталога/var/www/hello/missing.txt/тоже нет. =404— nginx отвечает 404 Not Found.
| URL | Файл на диске | Ответ |
|---|---|---|
/ | index.html есть | 200, HTML |
/style.css | style.css есть | 200, CSS |
/nope | ничего нет | 404 |
Подготовка и проверка:
sudo mkdir -p /var/www/hello
echo '<h1>Hello from Nginx</h1>' | sudo tee /var/www/hello/index.html
sudo nginx -t && sudo systemctl reload nginx
curl -i http://localhost/
Что делает код:
mkdir -p— создаёт каталог для файлов сайта.tee— записывает HTML (от root, если нет прав у обычного пользователя).nginx -t— проверка конфига до reload.curl -i— показывает заголовки и тело; в теле должен быть ваш<h1>.
Частая ошибка: забыли index index.html — для / nginx вернёт 403 Forbidden (каталог есть, но «листинг» по умолчанию запрещён).
2. Редирект HTTP → HTTPS
Задача: весь трафик с порта 80 перенаправить на HTTPS. Браузер сам откроет https://…, пользователь не вводит протокол вручную.
Когда искать: «nginx redirect http to https», «nginx return 301 ssl».
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
| Строка | Смысл |
|---|---|
server_name example.com www.example.com | Оба домена — с www и без |
return 301 https://… | Ответ без тела: «навсегда переехали» (код 301) |
$host | Подставится домен из запроса (example.com или www.example.com) |
$request_uri | Путь и ?query=… сохраняются |
Что происходит по шагам (GET http://example.com/old-page?q=1):
- Запрос приходит на порт 80.
- Nginx не ищет файлы — сразу
return 301. - В заголовке
Location: https://example.com/old-page?q=1. - Браузер повторяет запрос уже на порт 443 (нужен отдельный блок с SSL — пример №8).
| Запрос | Заголовок Location |
|---|---|
http://example.com/ | https://example.com/ |
http://www.example.com/blog/1 | https://www.example.com/blog/1 |
Проверка:
curl -I http://example.com/old-page?q=1
В выводе ищите строку HTTP/1.1 301 и Location: https://….
Частая ошибка: есть редирект с 80, но нет server { listen 443 ssl; … } — браузер после редиректа получит «connection refused» или ошибку сертификата.
3. SPA — fallback на index.html
Задача: React, Vue, Angular, Vite после npm run build — при обновлении страницы по адресу /users/42 nginx отдаёт index.html, а маршрутизацию делает JavaScript в браузере.
Когда искать: «nginx react router», «nginx vue history mode», «try_files index.html spa».
server {
listen 80;
server_name app.local;
root /var/www/spa/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|svg|woff2)$ {
expires 7d;
add_header Cache-Control "public, immutable";
}
}
| Строка | Смысл |
|---|---|
root …/dist | Сюда копируют результат npm run build (папка dist/ или build/) |
try_files … /index.html | Нет файла для пути — отдать один index.html (shell приложения) |
| `location ~* .(js | css…)$` |
~* | Регистронезависимое совпадение (.PNG тоже) |
expires 7d | Браузер может кэшировать файл 7 дней |
Cache-Control "public, immutable" | Файлы с хешем в имени (app.a1b2c3.js) не перезапрашивать зря |
Что происходит по шагам (GET /dashboard после F5):
- Nginx ищет файл
/var/www/spa/dist/dashboard— нет. - Ищет каталог
dashboard/— нет. - Внутренний переход на
/index.html— отдаёт SPA, статус 200 (это важно: SPA ожидает 200, а не 404).
Что происходит (GET /assets/index-a1b2.js):
- Совпадает второй
location(расширение.js). - Отдаётся реальный файл из
dist/assets/с заголовками кэша.
| URL | Есть файл в dist? | Ответ |
|---|---|---|
/ | index.html | 200, HTML |
/dashboard | только index.html | 200, тот же HTML |
/assets/app.js | да | 200, JS + Cache-Control |
Проверка:
curl -I http://app.local/dashboard
curl -I http://app.local/assets/index.js
Первый запрос — 200 и Content-Type: text/html. Второй — 200 и длинный Cache-Control.
Частая ошибка: забыли /index.html в try_files — F5 на /profile даёт 404, хотя в приложении страница открывалась по клику.
4. Reverse proxy на Node.js / Python / Go
Задача: снаружи пользователь ходит на порт 80, nginx передаёт запрос приложению на 127.0.0.1:3000 (Express, FastAPI, Flask, Gin и т.д.).
Когда искать: «nginx reverse proxy nodejs», «nginx proxy_pass localhost 3000», «nginx fastapi».
upstream app_backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 80;
server_name api.local;
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
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;
proxy_set_header Connection "";
}
}
| Строка | Смысл |
|---|---|
upstream app_backend | Именованная группа бэкендов (позже добавите второй server для балансировки) |
keepalive 32 | До 32 idle-соединений nginx → app (меньше накладных расходов TCP) |
proxy_pass http://app_backend | Запрос уходит на app, ответ nginx отдаёт клиенту |
proxy_http_version 1.1 | HTTP/1.1 нужен для keepalive к бэкенду |
Host $host | App видит реальный домен (api.local), а не 127.0.0.1 |
X-Real-IP | IP клиента (для логов и rate limit в приложении) |
X-Forwarded-For | Цепочка IP через прокси |
X-Forwarded-Proto | http или https — app строит правильные ссылки |
Connection "" | Сброс Connection: close — иначе keepalive к upstream ломается |
Что происходит по шагам (GET http://api.local/api/users):
- Nginx принимает запрос на :80.
location /— проксирует весь путь на127.0.0.1:3000/api/users.- Node/Python отвечает JSON.
- Nginx возвращает тот же статус и тело клиенту.
Схема:
Браузер → :80 nginx → :3000 приложение
↑ proxy_set_header добавляет заголовки
Проверка (app уже слушает 3000):
# Терминал 1 — простой backend
python -m http.server 3000
# Терминал 2 — через nginx
curl -i http://api.local/
Что делает код:
python -m http.server 3000— временный HTTP-сервер для проверки (на лабораторной вместо него — ваш Express/FastAPI).curl -i— если nginx настроен, увидите ответ app через прокси (в access.log nginx — запись запроса).
API под префиксом /api/ — отдельный location и слэш в конце proxy_pass:
location /api/ {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Host $host;
}
| Запрос клиента | Что получит backend |
|---|---|
/api/users | /users |
/api/health | /health |
Слэш после 3000/ отрезает префикс /api/ — это самый частый «тихий» баг на лабораторных (backend ждёт /api/users, а приходит /users или наоборот).
Частая ошибка: 502 Bad Gateway — app не запущен или слушает другой порт. Проверка: curl http://127.0.0.1:3000 минуя nginx.
5. PHP через PHP-FPM
Задача: .html и картинки — как файлы, .php — выполнить через PHP-FPM (Laravel, Symfony, WordPress, учебные скрипты).
Когда искать: «nginx php-fpm config», «nginx laravel public», «fastcgi_pass unix sock».
server {
listen 80;
server_name php.local;
root /var/www/php-app/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
}
location ~ /\. {
deny all;
}
}
| Строка | Смысл |
|---|---|
root …/public | У Laravel/Symfony открыт только каталог public/ — там index.php |
index index.php index.html | Сначала PHP, потом HTML |
try_files … /index.php?$query_string | Front controller — все «красивые» URL идут в index.php |
location ~ \.php$ | Любой URL, заканчивающийся на .php |
include fastcgi_params | Стандартный набор параметров для PHP |
fastcgi_pass unix:…sock | Сокет процесса PHP-FPM (путь зависит от версии PHP и ОС) |
SCRIPT_FILENAME | Полный путь к скрипту на диске — обязательная строка |
fastcgi_intercept_errors on | Ошибки PHP можно обработать через error_page в nginx |
location ~ /\. | Запрет .env, .git, .htaccess по HTTP |
Что происходит по шагам (GET /index.php):
- Совпадает
location ~ \.php$. - Nginx не отдаёт файл как текст — передаёт в FPM через сокет.
- FPM выполняет PHP, возвращает HTML.
- Nginx отдаёт HTML клиенту.
Что происходит (GET /users/list в Laravel):
location /→try_filesне находит файл.- Внутренний запрос
/index.php?(query из$query_string). - Laravel роутит
/users/listвнутри приложения.
| URL | Результат |
|---|---|
/index.php | Выполнение PHP |
/style.css | Статический файл |
/.env | 403 Forbidden (блок ~ /\.) |
/index.php скачивается как файл | Сломан location ~ \.php$ или FPM не запущен |
Проверка:
sudo systemctl status php8.3-fpm
curl -i http://php.local/index.php
ls -la /run/php/php8.3-fpm.sock
Частая ошибка: root указывает на корень репозитория, а не на public/ — в браузере открывается структура проекта, .env может стать доступен без правильного location ~ /\..
Конфиги для типовых задач
Сценарии для VPS, нескольких сайтов, HTTPS, WebSocket и защиты.
6. Два сайта на одном IP
Задача: один сервер, один IP, разные домены — shop.local и blog.local с разными папками.
Когда искать: «nginx несколько сайтов», «nginx virtual host», «nginx server_name».
server {
listen 80;
server_name shop.local;
root /var/www/shop;
index index.html;
location / { try_files $uri $uri/ =404; }
}
server {
listen 80;
server_name blog.local;
root /var/www/blog;
index index.html;
location / { try_files $uri $uri/ =404; }
}
| Строка | Смысл |
|---|---|
Два блока server | Два «сайта» на одном порту 80 |
Разный server_name | Nginx смотрит заголовок Host и выбирает блок |
Разный root | Файлы магазина и блога не смешиваются |
Что происходит по шагам:
- Оба блока слушают
:80. - Запрос с
Host: shop.local→ первыйserver, файлы из/var/www/shop. - Запрос с
Host: blog.local→ второйserver.
Проверка без DNS (с /etc/hosts или просто curl):
curl -H "Host: shop.local" http://127.0.0.1/
curl -H "Host: blog.local" http://127.0.0.1/
Частая ошибка: оба домена показывают один сайт — в /etc/hosts на клиенте неверный IP или сработал default_server (первый listen 80 default_server ловит чужие Host).
7. Балансировка между двумя app-серверами
Задача: распределить нагрузку между app1:8080 и app2:8080, временно отключать «упавший» инстанс.
upstream web_pool {
least_conn;
server app1:8080 max_fails=3 fail_timeout=30s;
server app2:8080 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
server_name balanced.local;
location / {
proxy_pass http://web_pool;
proxy_set_header Host $host;
proxy_next_upstream error timeout http_502 http_503;
}
}
| Строка | Смысл |
|---|---|
least_conn | Запрос на сервер с меньшим числом активных соединений |
max_fails=3 | После 3 неудачных попыток сервер считается недоступным |
fail_timeout=30s | 30 с не слать на него трафик, потом проверить снова |
proxy_next_upstream … | При 502/503 повторить запрос на другом сервере из pool |
Что происходит: nginx по очереди или по least_conn выбирает backend; если app1 вернул 502, клиент может получить ответ от app2 (если включён proxy_next_upstream).
8. HTTPS с Let's Encrypt (Certbot)
Задача: шифрование TLS после получения сертификата (certbot certonly или certbot --nginx).
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
root /var/www/example;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
| Строка | Смысл |
|---|---|
listen 443 ssl http2 | HTTPS + мультиплексирование HTTP/2 |
fullchain.pem | Сертификат сайта + промежуточные CA |
privkey.pem | Закрытый ключ (доступ только root/nginx) |
ssl_protocols TLSv1.2 TLSv1.3 | Старые небезопасные версии SSL отключены |
Сертификат Let's Encrypt живёт ~90 дней; certbot ставит timer для продления. После продления: sudo nginx -t && sudo systemctl reload nginx.
Проверка:
curl -I https://example.com
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates
9. WebSocket через reverse proxy
Задача: чат, live-лenta, Socket.io — соединение «апгрейдится» с HTTP до WebSocket.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name ws.local;
location /ws {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
}
| Строка | Смысл |
|---|---|
map $http_upgrade … | Если клиент шлёт Upgrade: websocket → Connection: upgrade, иначе close |
Upgrade / Connection | Обязательные заголовки для WebSocket handshake |
proxy_read_timeout 86400 | 24 ч — прокси не рвёт «висящее» WS-соединение по умолчанию (60 с) |
Что происходит: обычный GET с Upgrade: websocket nginx проксирует на backend; дальше идёт двусторонний поток сообщений.
10. Rate limit — защита от флуда
Задача: ограничить число запросов с одного IP на /login (brute-force, DDoS на уровне приложения).
# Эту строку — один раз внутри http { }, не внутри server { }
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=10r/s;
server {
listen 80;
server_name secure.local;
location /login {
limit_req zone=login_limit burst=20 nodelay;
proxy_pass http://127.0.0.1:3000;
}
}
| Строка | Смысл |
|---|---|
limit_req_zone | Создаёт «карту» лимитов в памяти (10 МБ под IP) |
$binary_remote_addr | Ключ — IP клиента в компактном виде |
rate=10r/s | В среднем 10 запросов в секунду с IP |
burst=20 nodelay | Разрешить «всплеск» до 20, без очереди |
limit_req zone=… | Применить лимит в конкретном location |
При превышении — 503 (можно сменить на 429: limit_req_status 429; в location).
Частая ошибка: limit_req_zone внутри server — nginx -t выдаст directive is not allowed here.
11. Gzip и заголовки безопасности
Задача: уменьшить размер HTML/JSON/CSS и добавить базовые заголовки защиты браузера.
http {
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 256;
server {
listen 80;
server_name safe.local;
root /var/www/site;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
try_files $uri $uri/ =404;
}
}
}
| Строка | Смысл |
|---|---|
gzip on | Сжимать ответы, если клиент шлёт Accept-Encoding: gzip |
gzip_min_length 256 | Мелкие ответы не сжимать (накладные расходы) |
X-Frame-Options SAMEORIGIN | Запрет встраивать сайт в <iframe> на чужих доменах (clickjacking) |
X-Content-Type-Options nosniff | Браузер не «угадывает» MIME |
add_header … always | Заголовок даже при 404/500 |
Проверка gzip:
curl -H "Accept-Encoding: gzip" -I http://safe.local/
В ответе должно быть Content-Encoding: gzip (для достаточно большого HTML).
12. Nginx в Docker — свой conf через volume
Задача: свой default.conf без пересборки образа. Дополнение к стекам Compose.
nginx-proxy/
compose.yaml
conf/
default.conf
html/
index.html
compose.yaml:
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
volumes:
- ./conf/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./html:/usr/share/nginx/html:ro
| Строка | Смысл |
|---|---|
"8080:80" | localhost:8080 на ПК → порт 80 в контейнере |
./conf/default.conf:…:ro | Ваш конфиг поверх дефолтного в образе |
./html:…:ro | Статика с хоста, read-only |
conf/default.conf:
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Что делает код:
docker compose up -d
curl -I http://localhost:8080/
docker compose exec web nginx -t
- Поднимается контейнер с вашим conf и html.
curlпроверяет проброс порта.exec web nginx -t— синтаксис внутри контейнера (удобно при отладке mount).
Шпаргалка — слэш в proxy_pass
Самый частый вопрос на Stack Overflow и лабораторных:
location | proxy_pass | Запрос клиента | URI на backend |
|---|---|---|---|
/api/ | http://127.0.0.1:3000/ | /api/users | /users |
/api/ | http://127.0.0.1:3000 | /api/users | /api/users |
/ | http://127.0.0.1:3000 | /users | /users |
Правило: если в proxy_pass есть URI (хотя бы / в конце) — nginx заменяет совпавший префикс location на этот URI. Если URI в proxy_pass нет — на backend уходит полный путь клиента.
Шпаргалка — root и alias
| Директива | URL | Путь на диске |
|---|---|---|
root /var/www; + location /img/ | /img/a.png | /var/www/img/a.png |
alias /var/static/; + location /img/ | /img/a.png | /var/static/a.png |
alias отрезает префикс location из пути; для /static/ часто пишут именно alias, а не root.
Каркас проверки — скрипт для лабораторной
Сохраните и подставляйте свой домен:
#!/bin/bash
DOMAIN="${1:-localhost}"
URL="http://${DOMAIN}/"
echo "=== nginx -t ==="
sudo nginx -t || exit 1
echo "=== HTTP HEAD ==="
curl -sI "$URL" | head -n 15
echo "=== последние ошибки ==="
sudo tail -n 5 /var/log/nginx/error.log
Что делает код:
$1— домен из аргумента (./check.sh shop.local) илиlocalhost.nginx -t— выход с ошибкой, если конфиг битый.curl -sI— только заголовки, первые 15 строк (статус,Content-Type,Location).tail error.log— последние причины 502/403/permission denied.
Шпаргалка команд
| Задача | Команда | Пояснение |
|---|---|---|
| Проверка синтаксиса | sudo nginx -t | Обязательно перед reload |
| Перезагрузка | sudo systemctl reload nginx | Без обрыва текущих соединений |
| Статус | systemctl status nginx | active (running) / failed |
| Ошибки | sudo tail -f /var/log/nginx/error.log | 502, permission denied, unknown directive |
| Запросы | sudo tail -f /var/log/nginx/access.log | IP, код, URL, User-Agent |
| Весь конфиг | sudo nginx -T 2>/dev/null | less | С комментариями # configuration file |
| Виртуальный Host | curl -H "Host: shop.local" http://127.0.0.1/ | Тест vhost без DNS |
| Заголовки HTTPS | curl -I https://example.com | Сертификат, редиректы, HSTS |
Типичные ошибки новичка
| Ошибка | Симптом | Как исправить |
|---|---|---|
Пропустили nginx -t | reload failed, сайт «старый» или down | всегда -t, читать stderr |
Слэш в proxy_pass | 404 на backend, «route not found» | таблица выше, сверить с API |
root вместо alias | /static/static/app.js | alias для префикса или поправить путь |
| PHP скачивается | браузер предлагает сохранить .php | location ~ \.php$, запущен php-fpm |
| 502 Bad Gateway | белая страница nginx | curl напрямую на backend-порт |
| Permission denied | 403, в error.log «permission denied» | www-data должен читать root |
| Два default_server | по IP открывается «чужой» сайт | один listen 80 default_server |
limit_req_zone в server | nginx -t падает | зона только в http { } |
| SPA без fallback | F5 на /page → 404 | try_files … /index.html |
127.0.0.1 в Docker | 502 из контейнера nginx | имя сервиса compose |
Практика — три мини-задания
- Статика. Пример №1: свой
index.html,curl -i, в access.log должна появиться строка с кодом 200. - Proxy. Python
http.server 3000+ пример №4: сравнитеcurl :3000иcurl через nginx— тело одинаковое, в proxy-варианте в access.log nginx есть запись. - SPA. Положите
dist/Vite/React, пример №3:curl -I /random/path— 200 иtext/html.
Подробный справочник директив — Nginx.
Чек-лист перед сдачей лабораторной
-
nginx -t— ok. - Для proxy есть
Host,X-Real-IP,X-Forwarded-For(иProtoпри HTTPS). - PHP —
rootнаpublic/,.envзакрыт. - Сертификаты на месте, ключ не в репозитории.
- В отчёте — скрин или вывод
curl -I, фрагмент конфига, что делает каждый блок.
Связанные материалы
| Материал | Зачем открыть |
|---|---|
| Справочник по Nginx | все директивы, кэш, FastCGI |
| Веб-серверы | HTTP, виртуальные хосты |
| Docker Compose — готовые стеки | nginx + volumes + proxy |
| curl | заголовки, POST, отладка API |
| SPA | client-side routing |
| Шаблоны | каркасы для других инструментов |
См. также
Другие статьи этого же раздела в боковом меню (как на странице "О разделе"). Практическая карта типовых IT-задач: термины, пошаговое внедрение, проверка качества и типичные ошибки. Простой консольный чат на C# — учебное приложение с сокетами: TCP между клиентом и сервером, многопоточность и обмен сообщениями в консоли. Примеры вёрстки на HTML и CSS с разбором: центрирование, Flexbox, Grid, формы, шапка, подвал и адаптив для учебы и портфолио. Перед началом работы обязательно изучите главу Turtle . Галерея 3D-фигур на Panda3D — карточки, куб, пирамида, сфера, сетки и составные сцены; код для локального запуска. Готовые docker-compose.yml с разбором каждой строки — nginx, PostgreSQL, Redis, WordPress, MongoDB. Примеры для школьников и студентов: postgres example, поднять базу локально, app + db. dockerfile example — 10 готовых Dockerfile с построчным разбором: node, python, golang, react nginx, spring boot, php, dotnet. Для студентов, лабораторных и docker build с нуля. PromQL example — готовые запросы Prometheus и Grafana с построчным разбором: up, rate, node_exporter cpu, memory, disk, http_requests_total, histogram_quantile p99, алерты. Для студентов, лабораторных и devops docker compose. Готовые манифесты Kubernetes с разбором каждой строки — Pod, Deployment, Service, ConfigMap, Secret, Ingress. Примеры для Minikube, kind и kubectl apply. Примеры графиков Matplotlib на Python для школьников и студентов — sin, cos, парабола, столбцы, scatter, гистограмма, подграфики; код с подробным разбором. Примеры pandas на Python для школьников и студентов — DataFrame, фильтрация, groupby, очистка, merge, сводные таблицы и экспорт; код с подробным разбором каждой строки. p5.js и Processing — готовый код фигур с подробным разбором каждой строки: квадрат, треугольник, цветок, снежинка, фракталы, анимация; для школьников и студентов.Готовые решения
Простой консольный чат на CSharp
HTML + CSS — готовые макеты
Примеры фигур Turtle на Python
Примеры фигур Panda3D на Python
Docker Compose — готовые стеки
Dockerfile — 10 типовых образов
Prometheus + Grafana — запросы
Kubernetes YAML — минимальные манифесты
Matplotlib — графики
Pandas — типовые операции
Примеры фигур на Processing/p5.js