Files
kadence/deploy/nginx/kadence.timka.su.conf
T
timka123andClaude Opus 5 eb49df58d7 deploy: http2 via the listen directive, for nginx 1.24
The server runs nginx 1.24.0; the standalone `http2 on;` directive only
arrived in 1.25.1, so `nginx -t` rejected the vhost as an unknown directive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-19 15:00:13 +03:00

89 lines
3.3 KiB
Plaintext

# 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 ... http2` rather than the `http2 on;` directive: this host runs
# nginx 1.24, and `http2 on` only arrived in 1.25.1.
listen 443 ssl http2;
listen [::]:443 ssl http2;
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;
}
}