# kadence.timka.su — TLS terminates here; the app listens on loopback only. # # Installed to /etc/nginx/sites-available/kadence.timka.su by deploy/deploy.sh, # which also obtains the certificate. Deliberately *not* managed by # `certbot --nginx`: that rewrites the file in place, so the copy in git would # stop being the truth. `certbot certonly --webroot` renews the cert and leaves # this file alone. upstream kadence { # Must match KADENCE_BIND in .env (docker-compose.prod.yml publishes it). # 8000 is taken on this host by remnawave, hence 8010. server 127.0.0.1:8010; keepalive 16; } server { listen 80; listen [::]:80; server_name kadence.timka.su; # Renewal has to work over plain HTTP, so this one location stays on :80. location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; listen [::]:443 ssl; http2 on; server_name kadence.timka.su; ssl_certificate /etc/letsencrypt/live/kadence.timka.su/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/kadence.timka.su/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_session_cache shared:kadence_tls:10m; ssl_session_timeout 1d; ssl_stapling on; ssl_stapling_verify on; # The session cookie is HttpOnly/SameSite=Lax and the app marks it Secure off # X-Forwarded-Proto below; HSTS is what stops a first request going plain. add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options nosniff always; add_header Referrer-Policy strict-origin-when-cross-origin always; # The settings page posts secrets (Jira tokens, bot tokens) as JSON; nothing # here is an upload, so a small cap is free protection. client_max_body_size 2m; access_log /var/log/nginx/kadence.access.log; error_log /var/log/nginx/kadence.error.log; # Vite fingerprints these, so they can be cached hard and forever. location /assets/ { proxy_pass http://kadence; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Connection ""; expires 1y; add_header Cache-Control "public, immutable"; } location / { proxy_pass http://kadence; 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; # This is what makes the login cookie `Secure` — `routes_auth._is_https` # reads it, because the app itself only ever sees plain HTTP on loopback. proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header Connection ""; # A task refresh fans out to Jira, Todoist, Google and the laundry site in # one request; 60s is the shortest timeout that does not cut it off. proxy_connect_timeout 10s; proxy_send_timeout 120s; proxy_read_timeout 120s; } }