Merge pull request #1286 from CmdKratosDev/docs/caddy-security-headers

docs: add Optional Security Headers via Reverse Proxy section to the Caddy HTTPS guide
This commit is contained in:
Classic298
2026-06-06 16:46:04 +02:00
committed by GitHub
+53
View File
@@ -107,6 +107,59 @@ docker compose up -d
You should now be able to access Open WebUI at `https://your-domain.com`.
## Optional: Security Headers via Reverse Proxy
Open WebUI supports setting security headers directly at the application layer via environment variables
(recommended path — see [Security Headers](/getting-started/advanced-topics/hardening#security-headers)).
If you manage multiple applications behind a single Caddy instance, or if your deployment does not allow
direct environment variable access, you can set these headers at the proxy layer instead.
**Choose one layer only.** Setting the same header in both places produces duplicate directives:
duplicate `Content-Security-Policy` headers are combined by browsers (intersection), which can
unexpectedly tighten or break your policy, while duplicate `Strict-Transport-Security` or
`X-Frame-Options` headers lead to ambiguous, browser-dependent behavior. If you already set any of
`HSTS`, `XFRAME_OPTIONS`, `XCONTENT_TYPE`, `REFERRER_POLICY`, `PERMISSIONS_POLICY`, or
`CONTENT_SECURITY_POLICY` via environment variables, skip this section.
Add the following `header` block inside your existing site block:
```caddyfile
your.domain.example {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Frame-Options "DENY"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "camera=(), microphone=(), geolocation=()"
-Server
}
reverse_proxy localhost:8080
}
```
**HSTS is sticky and hard to undo.** `max-age=31536000` (1 year) with `includeSubDomains` instructs every browser that has visited this domain to refuse plain HTTP for the domain **and all its subdomains** for a full year — this cannot be reverted server-side quickly (you must serve `max-age=0` and wait for each client to revisit). Only keep `includeSubDomains` if every current and future subdomain serves valid HTTPS. If unsure, start with a short value such as `max-age=300` and increase it after confirming HTTPS works everywhere. Do **not** add `preload` unless you understand it is near-permanent and requires separate submission to the browser preload list.
The `-Server` directive removes the `Server` response header that Caddy adds by default.
Caddy handles WebSocket upgrades and SSE connections automatically — no additional configuration
is required for Open WebUI's streaming responses.
The `Permissions-Policy` example above is a deny-list of common sensitive features, not an exhaustive
list. If your deployment uses voice input or webcam capture, set the relevant directive to `(self)`
instead of removing it.
**Content-Security-Policy.** CSP is highly deployment-specific (custom plugins, embedded iframes,
external model providers, Artifacts/code rendering, voice input), so this section intentionally does
**not** ship a ready-made proxy-layer CSP. Set CSP via the `CONTENT_SECURITY_POLICY` environment
variable and use `CONTENT_SECURITY_POLICY_REPORT_ONLY` to derive a working policy before enforcing it —
see [Security Headers](/getting-started/advanced-topics/hardening#security-headers).
Verify headers are applied after reloading Caddy:
```bash
curl -sI https://your.domain.example | grep -E "Strict-Transport|X-Frame|X-Content|Referrer|Permissions"
```
## Updating Open WebUI
I wanted to include a quick note on how to update Open WebUI without losing your data. Since we're using a volume to store the data, you can simply pull the latest image and restart the container.