# Отдача статики тестового веба. # Роутинг у нас hash-based (#/tickets), но на всякий случай любой # несуществующий путь ведём на index.html — случайный 404 мешает отладке. # Зоны лимитов лежат здесь (файл подключается в http-контексте через # conf.d): 10 запросов/сек на IP и до 20 одновременных соединений. # Браузеру с запасом хватает, а зациклившийся retry фронта упрётся # в потолок, а не будет долбить статику бесконечно. 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 — на пачку файлов при первой загрузке страницы (~5 запросов), # сверх лимита — сразу 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 в # логах — дописанный клиентом мусор останется левым элементом. # local dev: бэкенд на хосту (host.docker.internal:8080); # в проде адрес подставит app-nginx.prod. location ~ ^/(auth|users|projects|objects|tickets|invitations|recurring|ping|db-ping|swagger)(/|$) { proxy_pass http://host.docker.internal: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 в этой локации перекрывает серверный (nginx не # смешивает уровни) — заголовки дублируем здесь явно. 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; } }