Code

Turn on protection — and hit the 403

First the dependency:

go get github.com/gorilla/csrf

Three files as tabs, in dependency order: config.gomain.gorouter.go — a secret to key the token, the derived key, and the middleware itself. All three are modifications.

1. internal/config/config.go (modify) — a SessionSecret, read from SESSION_SECRET. In dev it falls back to a fixed insecure value so things work out of the box; in prod it's required (a guard returns an error if it's missing). Everything else is carried forward.

2. cmd/signflow/main.go (modify) — derive a stable 32-byte key from the secret, so there's only one secret to manage:

csrfKey := sha256.Sum256([]byte("csrf:" + cfg.SessionSecret))
// ...
Handler: h.Router(staticFS, csrfKey[:]),   // ← Router now takes the key

3. internal/handlers/router.go (modify) — the protection itself:

csrfMW := csrf.Protect(
	csrfKey,
	csrf.Secure(h.Cfg.IsProd()),          // Secure cookie only in prod
	csrf.FieldName("gorilla.csrf.Token"), // the hidden field forms will carry
)
r.Use(csrfMW)

That's it — CSRF is now enforced on every POST.

Verify — and watch it break. Regenerate and run, then try to log in:

go run ./cmd/signflow

Open http://localhost:8080/login, enter your credentials, submit. Instead of your dashboard:

Forbidden - referer not supplied

A 403. On your own login form. With correct credentials. And the message — "referer not supplied" — tells you nothing about what's actually wrong. This is the error every Go developer hits once and burns an afternoon on.

Confirm it from the command line too:

$ curl -si -X POST localhost:8080/login -d 'email=a@b.co&password=x' | head -1
HTTP/1.1 403 Forbidden

Don't fix it yet. Sit with it for a second. Your form has no token field, and you're on http:// — but the error doesn't mention tokens at all; it complains about the referer. That's the clue. The next step explains exactly why, and fixes it in two moves.