axe-web/nginx.prod.conf
edadmin ca3392157a
Some checks are pending
web / check (push) Waiting to run
web / build-web (push) Waiting to run
web / deploy (push) Blocked by required conditions
feat: prod web image + Gitea Actions, demo chips removed
2026-10-18 22:44:06 +03:00

70 lines
3.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Прод-конфиг веба: статика + прокси API на контейнер api внутри сети
# compose (строка api резолвится docker- DNS). Порт наружу — 80, TLS
# терминалирует перед этим nginx (балансировщик / Caddy / Traefik).
#
# Отличия от dev nginx.conf:
# - proxy_pass http://api:8080 вместо host.docker.internal:8080;
# - security-заголовки (nosniff / frame / referrer) поверх HSTS.
#
# Хост, который не относится к нашему origin (Host-заголовок чужой),
# nginx отдаёт сюда же — server_name _; выделенного виртуального хоста
# под чужой домен нет, это осознанно.
# Зоны лимитов: 10 запросов/сек на IP и до 20 одновременных соединений.
limit_req_zone $binary_remote_addr zone=perip:1m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=addr:1m;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_types text/css application/javascript application/json;
gzip_min_length 1024;
# HSTS браузер игнорирует по HTTP, а с TLS заставит держать сайт
# только по https; includeSubDomains — API и статика на одном домене.
# ВНИМАНИЕ: add_header на уровне server перекрывается ЛЮБЫМ
# add_header внутри location — поэтому в location / заголовки
# продублированы явно.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# burst — на пачку файлов при первой загрузке страницы, сверх
# лимита — сразу 429, а не неопределённое ожидание.
limit_req zone=perip burst=30 nodelay;
limit_req_status 429;
limit_conn addr 20;
limit_conn_status 429;
# API на том же origin (BASE='' в js/api.js): проксируем API-пути
# бэкенда, остальное — статика. Regex-локация важнее prefix /.
# X-Forwarded-For ПЕРЕЗАПИСЫВАЕМ ($remote_addr), а не дополняем:
# gin берёт левый элемент и по нему считает лимиты и client_ip в
# логах — дописанный клиентом мусор останется левым элементом.
location ~ ^/(auth|users|projects|objects|tickets|invitations|recurring|ping|db-ping|swagger)(/|$) {
proxy_pass http://api:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
# Фотоотчёт: до 10 МБ + запас на multipart-обвязку.
client_max_body_size 12m;
}
location / {
try_files $uri $uri/ /index.html;
# Кэш мешает отладке: отдаём всегда свежее.
# Дублируем заголовки: add_header в location перекрывает серверный.
add_header Cache-Control "no-cache";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
}