A shared (security_headers) snippet, imported first in every site block of the public, staging and production Caddyfiles: HSTS (one year, no includeSubDomains), nosniff, strict-origin-when-cross-origin, framing refused (X-Frame-Options DENY + frame-ancestors 'none'), camera/microphone/ geolocation/payment off, and the Server / X-Powered-By headers dropped. Approved by the product owner 2026-11-01. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y
34 lines
1000 B
Caddyfile
34 lines
1000 B
Caddyfile
# Security response headers on every site (issue #119, checklist item 33).
|
|
# Nothing embeds the site, so framing is refused; HSTS has no includeSubDomains
|
|
# because the subdomains are listed one by one.
|
|
(security_headers) {
|
|
header {
|
|
Strict-Transport-Security "max-age=31536000"
|
|
X-Content-Type-Options "nosniff"
|
|
Referrer-Policy "strict-origin-when-cross-origin"
|
|
X-Frame-Options "DENY"
|
|
Content-Security-Policy "frame-ancestors 'none'"
|
|
Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()"
|
|
-Server
|
|
-X-Powered-By
|
|
}
|
|
}
|
|
|
|
{$SITE_ADDRESS:https://jyotisha.chat} {
|
|
import security_headers
|
|
encode zstd gzip
|
|
|
|
@htmlDocuments path / /login
|
|
header @htmlDocuments Cache-Control "private, no-store, must-revalidate"
|
|
|
|
reverse_proxy web:3000 {
|
|
lb_try_duration 10s
|
|
lb_try_interval 250ms
|
|
}
|
|
}
|
|
|
|
www.jyotisha.chat {
|
|
import security_headers
|
|
redir https://jyotisha.chat{uri} permanent
|
|
}
|