# 001 — CSRF verification fails behind a TLS-terminating reverse proxy **Status:** Fixed ## Description Deploying Daybook behind Apache (TLS-terminating reverse proxy → plain HTTP to the container) on `cloud-vm-1`, login failed with: ``` Forbidden (403) CSRF verification failed. Request aborted. ``` **Root cause:** `daybook_project/settings.py` set `ALLOWED_HOSTS` from an env var but never set `CSRF_TRUSTED_ORIGINS` or `SECURE_PROXY_SSL_HEADER`. Without the latter, Django has no way to know the original request was HTTPS — the proxy terminates TLS and forwards plain HTTP — so it treats every request as insecure, which breaks CSRF checks on any POST (including login) once a browser connects over `https://`. This isn't specific to `cloud-vm-1` — it'll happen on any reverse-proxied deployment (Apache, Nginx, Traefik, a cloud load balancer), which is the explicitly recommended setup in `deploy/cloud_agent_deploy.md` §8. The runbook doesn't mention CSRF or proxy-awareness anywhere. ## Fix **App (`daybook_project/settings.py`):** ```python CSRF_TRUSTED_ORIGINS = [origin.strip() for origin in os.environ.get("DJANGO_CSRF_TRUSTED_ORIGINS", "").split(",") if origin.strip()] SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") ``` Committed locally as `d458cd3` ("Fix CSRF failure behind a TLS-terminating reverse proxy") — **needs to be pushed to `main`**, the deploy agent that made this fix didn't have push access to the Forgejo repo. **Deploy env (`deploy/env.example` / runbook):** needs a new var, `DJANGO_CSRF_TRUSTED_ORIGINS`, set to the public HTTPS origin, e.g.: ``` DJANGO_CSRF_TRUSTED_ORIGINS=https://daybook.example.com ``` **Proxy config:** `SECURE_PROXY_SSL_HEADER` only works if the proxy actually sends that header. The Apache example in `cloud_agent_deploy.md` §8 is missing it — needs: ```apache RequestHeader set X-Forwarded-Proto "https" ``` inside the `` block (requires `a2enmod headers`). The equivalent for Nginx is `proxy_set_header X-Forwarded-Proto $scheme;`, worth adding to `docs/deployment.md`'s reverse-proxy notes too since that doc mentions Nginx/Traefik as options without this caveat. ## Resolution - [x] Pushed commit `d458cd3` to `main`. - [x] Added `DJANGO_CSRF_TRUSTED_ORIGINS` to `deploy/env.example` with an explanatory comment. - [x] Updated `cloud_agent_deploy.md` §8's Apache example to include `RequestHeader set X-Forwarded-Proto "https"`, plus a Caddy equivalent and a checklist item that verifies by actually logging in through the proxy (not just curling `/api/capabilities`, which is unauthenticated GET and wouldn't have caught this). - [x] Updated `docs/deployment.md`'s "Reverse Proxy Notes" and checklist with the same requirement, plus an Nginx/Apache/Caddy header cheat sheet.