Erste Produktivversion
This commit is contained in:
195
web/nginx.conf
Normal file
195
web/nginx.conf
Normal file
@@ -0,0 +1,195 @@
|
||||
# Rate Limiting fuer Anmelde- und Registrierungsrouten.
|
||||
# Zweite Verteidigungslinie - die Anwendung begrenzt zusaetzlich pro
|
||||
# Konto, was nginx nicht kann.
|
||||
limit_req_zone $binary_remote_addr zone=auth_zone:10m rate=10r/m;
|
||||
limit_req_status 429;
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
server_name _;
|
||||
server_tokens off;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
gzip on;
|
||||
gzip_types text/css application/javascript application/json image/svg+xml;
|
||||
gzip_min_length 1024;
|
||||
|
||||
client_max_body_size 2m;
|
||||
|
||||
# Eingebauter DNS-Server von Docker. Ohne diesen loest nginx den
|
||||
# Namen "api" nur EINMAL beim Start auf und merkt sich die IP
|
||||
# dauerhaft - nach jedem Neustart des api-Containers gaebe es 502.
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
resolver_timeout 5s;
|
||||
|
||||
# Steht in proxy_pass eine Variable, loest nginx pro Request neu auf.
|
||||
# Dann muss die Ziel-URI aber explizit angehaengt werden - deshalb
|
||||
# ueberall $request_uri (enthaelt auch den Query-String).
|
||||
set $api_backend "http://api:8000";
|
||||
|
||||
# Sicherheitskopfzeilen. Siehe Kommentar in der Datei: sie muss in
|
||||
# JEDER location wiederholt werden, die eigene add_header setzt.
|
||||
include /etc/nginx/security_headers.conf;
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# API
|
||||
# ------------------------------------------------------------------
|
||||
# Diese Pfade tragen einen Token im URI. Nicht protokollieren -
|
||||
# siehe Begründung bei /s/.
|
||||
location ~ ^/api/(public|invites)/ {
|
||||
proxy_pass $api_backend$request_uri;
|
||||
include /etc/nginx/proxy_common.conf;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location /api/auth/welcome {
|
||||
proxy_pass $api_backend$request_uri;
|
||||
include /etc/nginx/proxy_common.conf;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location /api/auth/email-change {
|
||||
proxy_pass $api_backend$request_uri;
|
||||
include /etc/nginx/proxy_common.conf;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location /api/auth/verify {
|
||||
proxy_pass $api_backend$request_uri;
|
||||
include /etc/nginx/proxy_common.conf;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass $api_backend$request_uri;
|
||||
include /etc/nginx/proxy_common.conf;
|
||||
}
|
||||
|
||||
# Ereigniskanal (Server-Sent Events).
|
||||
location ~ ^/api/lists/[^/]+/events$ {
|
||||
proxy_pass $api_backend$request_uri;
|
||||
include /etc/nginx/proxy_common.conf;
|
||||
# proxy_buffering, proxy_cache und die langen Zeitüberschreitungen
|
||||
# stehen bereits in proxy_common.conf - hier nicht wiederholen,
|
||||
# nginx lässt jede Direktive nur einmal je Kontext zu.
|
||||
# Neu ist nur: keine Komprimierung, die würde die Ereignisse
|
||||
# sammeln statt einzeln durchzureichen.
|
||||
gzip off;
|
||||
}
|
||||
|
||||
location /api/auth/ {
|
||||
limit_req zone=auth_zone burst=20 nodelay;
|
||||
proxy_pass $api_backend$request_uri;
|
||||
include /etc/nginx/proxy_common.conf;
|
||||
}
|
||||
|
||||
# Interaktive API-Dokumentation. Vor Produktivbetrieb auskommentieren
|
||||
# oder per allow/deny auf das interne Netz beschraenken.
|
||||
location ~ ^/(docs|redoc|openapi\.json)$ {
|
||||
proxy_pass $api_backend$request_uri;
|
||||
include /etc/nginx/proxy_common.conf;
|
||||
}
|
||||
|
||||
location ~ ^/(healthz|readyz)$ {
|
||||
access_log off;
|
||||
proxy_pass $api_backend$request_uri;
|
||||
include /etc/nginx/proxy_common.conf;
|
||||
}
|
||||
|
||||
# Klartext statt HTML-Fehlerseite, damit ein fetch() im Browser eine
|
||||
# verwertbare Meldung bekommt und nicht an JSON.parse scheitert.
|
||||
error_page 502 503 504 = @backend_down;
|
||||
|
||||
location @backend_down {
|
||||
default_type application/json;
|
||||
return 503 '{"status":"error","detail":"Backend nicht erreichbar. Bitte docker compose logs api pruefen."}';
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Statische PWA
|
||||
# ------------------------------------------------------------------
|
||||
location = /sw.js {
|
||||
include /etc/nginx/security_headers.conf;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Vom api-Container erzeugt, damit der Anwendungsname aus der .env
|
||||
# auch auf dem Startbildschirm erscheint - ohne den web-Container
|
||||
# bei jeder Umbenennung neu bauen zu müssen.
|
||||
location = /manifest.webmanifest {
|
||||
proxy_pass $api_backend$request_uri;
|
||||
include /etc/nginx/proxy_common.conf;
|
||||
}
|
||||
|
||||
# Von Vite gebaute Dateien tragen einen Hash im Namen - lange cachen.
|
||||
location /assets/ {
|
||||
include /etc/nginx/security_headers.conf;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Icons aendern sich praktisch nie.
|
||||
location /icons/ {
|
||||
include /etc/nginx/security_headers.conf;
|
||||
add_header Cache-Control "public, max-age=604800" always;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Öffentliche Listenlinks: nie zwischenspeichern, nie indexieren.
|
||||
location /s/ {
|
||||
include /etc/nginx/security_headers.conf;
|
||||
add_header Cache-Control "no-store" always;
|
||||
add_header X-Robots-Tag "noindex, nofollow, noarchive" always;
|
||||
# Der Token IST die Berechtigung. Stünde er im Zugriffsprotokoll,
|
||||
# käme jeder mit Leserecht auf die Logdateien an alle geteilten
|
||||
# Listen - und Logdateien wandern in Sicherungen, werden an
|
||||
# Auswertungswerkzeuge gereicht und leben länger als der Link.
|
||||
access_log off;
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
location = /robots.txt {
|
||||
include /etc/nginx/security_headers.conf;
|
||||
default_type text/plain;
|
||||
return 200 "User-agent: *\nDisallow: /s/\nDisallow: /invite\nDisallow: /reset\nDisallow: /willkommen\n";
|
||||
}
|
||||
|
||||
# Aus demselben Grund wie bei /s/: Einladungs- und
|
||||
# Passwort-Zurücksetzen-Links tragen ihren Token im Query-String,
|
||||
# und den schreibt nginx sonst mit ins Protokoll.
|
||||
location = /invite {
|
||||
include /etc/nginx/security_headers.conf;
|
||||
add_header Cache-Control "no-store" always;
|
||||
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||
access_log off;
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
location = /willkommen {
|
||||
include /etc/nginx/security_headers.conf;
|
||||
add_header Cache-Control "no-store" always;
|
||||
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||
access_log off;
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
location = /reset {
|
||||
include /etc/nginx/security_headers.conf;
|
||||
add_header Cache-Control "no-store" always;
|
||||
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||
access_log off;
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
# Ohne Bundler tragen die JS-Dateien keinen Hash im Namen. "no-cache"
|
||||
# heisst nicht "nicht speichern", sondern "vor Benutzung rueckfragen" -
|
||||
# der Browser holt also nur bei Aenderung neu.
|
||||
location / {
|
||||
include /etc/nginx/security_headers.conf;
|
||||
add_header Cache-Control "no-cache" always;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user