# caddy/security.snippet
# Reusable security headers for every site behind Caddy. Include with:
# (security) { import security }
# and then in each vhost:
# import security
#
# The CSP is intentionally generic; if a specific app (grafana, jellyfin)
# breaks, add a per-vhost override after the import. mercemay.top/src/homelab-compose/
# has the full Caddyfile layout.
(security) {
header {
# Prevent mime-sniffing; everything we serve is a known type.
X-Content-Type-Options "nosniff"
# No framing unless I explicitly loosen it for an app.
X-Frame-Options "DENY"
Content-Security-Policy "default-src 'self'; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; script-src 'self'; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"
# Opt out of legacy referrer leakage.
Referrer-Policy "strict-origin-when-cross-origin"
# No third-party APIs from any homelab app by default.
Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"
# HSTS: 1y, includeSubDomains. Preloading handled at DNS level.
Strict-Transport-Security "max-age=31536000; includeSubDomains"
# Strip server banner so we stop advertising versions.
-Server
}
}
# A stricter variant for the admin-only stuff (grafana, gitea admin, etc).
# Adds forward_auth to Authelia and disables caching so bookmarked pages
# always re-check auth.
(admin-only) {
import security
forward_auth authelia:9091 {
uri /api/verify?rd=https://auth.{$DOMAIN}/
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
}
header Cache-Control "no-store"
}