This episode hardens the transport layer: TLS termination at the reverse proxy, recommended TLS versions and ciphers, security headers like HSTS, X-Frame-Options, CSP, and X-Content-Type-Options, as well as the Secure, HttpOnly, and SameSite cookie flags to protect sessions.

Episode 21 protected Authelia from the logic side: regulation blocks password guessers. But there's one more fundamental door: the communication channel itself. If traffic runs over plain HTTP, everything sent — including passwords — can be read in transit. Episode 22 builds a secure transport layer: HTTPS, security headers, and TLS policies.
The analogy is sending a letter: regulation ensures only the rightful recipient may open the mailbox, while TLS is the sealed envelope ensuring the letter's contents aren't read by anyone during transit. Both are required.
In the Authelia architecture, TLS certificates are almost always installed at the reverse proxy — NGINX, Traefik, Caddy, or HAProxy — not on Authelia itself. The proxy receives HTTPS from the browser, opens the envelope, then forwards the request to Authelia over internal HTTP.
Termination at the proxy is beneficial because certificates are managed in one place for all services. So Authelia knows the original connection is secure, the proxy must set the X-Forwarded-Proto header:
server {
listen 443 ssl http2;
server_name auth.example.com;
ssl_certificate /etc/letsencrypt/live/auth.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/auth.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
location / {
proxy_pass http://authelia:9091;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}Authelia receives HTTP traffic on port 9091 from inside the proxy network. Because the original connection is already HTTPS, Secure-marked cookies remain valid. Validate the configuration before reloading with nginx -t.
| Header | Example Value | Function |
|---|---|---|
Strict-Transport-Security | max-age=31536000; includeSubDomains | Forces the browser to use HTTPS |
X-Frame-Options | DENY | Prevents the page from being framed by other sites (clickjacking) |
X-Content-Type-Options | nosniff | Prevents browsers from guessing MIME types |
Referrer-Policy | strict-origin-when-cross-origin | Limits URLs leaking via the referrer |
Permissions-Policy | geolocation=(), camera=() | Disables unnecessary browser features |
Content-Security-Policy | As needed | Restricts which content sources may load |
Strict-Transport-Security tells the browser: for this domain, never use HTTP during the max-age period. After the browser visits once over HTTPS, subsequent HTTP connection attempts are auto-upgraded. This closes the downgrade attack and first-page interception gap.
A common max-age value is 31536000 (one year). For first visits, make sure no application is still accessible over plain HTTP — HSTS only helps if all previous connections were already secure.
Content-Security-Policy (CSP) controls which resources a page may load. For Authelia, which serves its own login portal, an overly strict CSP can break the page. Start with a basic policy like frame-ancestors 'self', then loosen gradually while watching the browser console.
X-Frame-Options layers with frame-ancestors: both prevent Authelia's login page from being embedded inside an attacker's iframe. With DENY, an attacker can't display the Authelia portal in a transparent frame and trick users into entering credentials.
Warning
Security headers are applied at the proxy, not in Authelia. Make sure they're applied to the Authelia server block, and be careful with CSP — test in the browser console first, because a wrong policy can break the login portal.
Authelia's session cookie is only as valuable as its flags:
Secure — the cookie is only sent over HTTPS. Authelia sets it automatically when the original connection is secure, so make sure X-Forwarded-Proto is forwarded correctly.HttpOnly — the cookie can't be accessed by JavaScript, so tokens don't leak through scripts.SameSite — lax by default in Authelia, restricting the cookie from being sent cross-site. The strict value is stricter; none is only for special cases.Limit TLS versions to only the safe ones: TLSv1.2 and TLSv1.3. Old versions like TLS 1.0 and 1.1 have long-known vulnerabilities. For ciphers, steer preference toward modern suites with forward secrecy (for example ECDHE keys); the HIGH:!aNULL:!MD5 pattern in the NGINX config above is a safe starting point.
In Caddy, secure TLS policy is handled automatically — just make sure the domain and HTTPS are active:
auth.example.com {
header Strict-Transport-Security "max-age=31536000; includeSubDomains"
header X-Frame-Options "DENY"
header X-Content-Type-Options "nosniff"
header Referrer-Policy "strict-origin-when-cross-origin"
reverse_proxy authelia:9091
}Caddy handles certificates and automatic renewal via Let's Encrypt. Headers are applied with the header directive.
Before closing this episode, make sure the following baseline is met:
Strict-Transport-Security header with a long max-age.X-Frame-Options and X-Content-Type-Options are active.Secure, HttpOnly, and SameSite.X-Forwarded-Proto is forwarded correctly from the proxy.Tip
Check your configuration with a header inspection tool like securityheaders.com or a browser extension. Missing headers will be obvious there, and the results can be compared before and after fixes.
In this episode you hardened the transport layer:
Secure, HttpOnly, and SameSite cookie flags protect sessions.This layer secures the path, but there's one more philosophical question: how much data do we actually need to store? In episode 23, we discuss Privacy & Anonymization — data-sparing logging, minimal retention, up to deleting user data. See you there!