196 lines
6.9 KiB
Nginx Configuration File
196 lines
6.9 KiB
Nginx Configuration File
# 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;
|
|
}
|
|
}
|