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
37 lines
1.1 KiB
Caddyfile
37 lines
1.1 KiB
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://staging.jyotisha.chat} {
|
|
import security_headers
|
|
encode zstd gzip
|
|
|
|
@htmlDocuments path / /login
|
|
header @htmlDocuments Cache-Control "private, no-store, must-revalidate"
|
|
|
|
@adminPaths path /admin /admin/* /api/admin/*
|
|
respond @adminPaths "Not found" 404
|
|
|
|
reverse_proxy web:3000
|
|
}
|
|
|
|
https://admin.staging.jyotisha.chat {
|
|
import security_headers
|
|
encode zstd gzip
|
|
@root path /
|
|
redir @root /admin 308
|
|
reverse_proxy web:3000
|
|
}
|