add CSP-clean static build, hardened nginx config, and container image

This commit is contained in:
2026-07-27 11:10:20 +10:00
parent e45f5729fa
commit a3c6d33ecf
6 changed files with 165 additions and 1 deletions
+51
View File
@@ -0,0 +1,51 @@
# nginx config for the static teaching site, baked into the image.
# Base: nginxinc/nginx-unprivileged (uid 101, listens on 8080).
# Read-only rootfs in k8s: /tmp and /var/cache/nginx are emptyDir mounts.
server {
listen 8080;
server_name _;
root /usr/share/nginx/html;
index index.html;
server_tokens off;
# Behind the Cloudflare Tunnel on :443 while nginx listens on :8080 — without these,
# nginx emits ABSOLUTE redirects to http://host:8080/... and breaks every link.
absolute_redirect off;
port_in_redirect off;
include /etc/nginx/security-headers.conf;
# ---- Caching -------------------------------------------------------------
# Scene images are content-hashed at build time → safe to cache hard.
location /assets/ {
include /etc/nginx/security-headers.conf;
expires 1y;
add_header Cache-Control "public, immutable" always;
}
# Scripts are NOT fingerprinted yet — revalidate so a deploy actually propagates.
location ~* \.js$ {
include /etc/nginx/security-headers.conf;
add_header Cache-Control "no-cache" always;
}
location ~* \.html$ {
include /etc/nginx/security-headers.conf;
add_header Cache-Control "no-cache" always;
}
# ---- Routing -------------------------------------------------------------
location / {
try_files $uri $uri/ $uri.html =404;
}
# ---- Compression ---------------------------------------------------------
# WebP/PNG are already compressed — gzipping them burns CPU for nothing.
gzip on;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
gzip_vary on;
}