docs(gateway): document security headers + HSTS opt-in option

This commit is contained in:
iris 2026-06-05 16:25:37 +02:00 committed by mara
commit fdd100194e

View file

@ -477,3 +477,45 @@ dialog see the human-readable hint. The internal exact-match location
ordering, preventing the subrequest from looping back through
`auth_basic`.
## Security headers
The following headers are emitted at server scope on every gateway
vhost (`_`, `forge.<domain>`, `matrix.<domain>`):
| Header | Value |
|--------|-------|
| `X-Frame-Options` | `SAMEORIGIN` |
| `X-Content-Type-Options` | `nosniff` |
| `Referrer-Policy` | `strict-origin-when-cross-origin` |
nginx's `add_header` inheritance rule: a `location` block that sets its
own `add_header` does **not** inherit server-scope headers. API locations
that carry their own CORS headers (e.g. `/.well-known/matrix/client`,
`/_matrix/`) are therefore unaffected. HTML-serving and proxy locations
with no `add_header` of their own pick the security headers up
automatically.
### HSTS (`gateway.hsts`)
HSTS is **opt-in** and disabled by default:
```nix
services.hyperhive.gateway.hsts = {
enable = true; # default: false
maxAge = 31536000; # default: 1 year (required for preload list)
includeSubDomains = true; # default: true
};
```
When enabled, a `Strict-Transport-Security: max-age=...[; includeSubDomains]`
header is added alongside the other security headers.
**Opt-in rationale**: HSTS pins HTTPS in the browser's preload cache;
enabling it on a deployment that later loses TLS locks browsers out
until `max-age` expires. Only enable when TLS is permanent.
**Assertion**: `hsts.enable = true` without a TLS mode configured
(`selfSignedTls`, `tls.certDir`, or `tls.acme.enable`) is a NixOS
build-time assertion failure — HSTS over plain HTTP is harmless but
almost always a misconfiguration.