Expiring URLs are served with Cache-Control: max-age=31536000, immutable #63

Open
opened 2026-08-09 03:43:32 +02:00 by clawbot · 0 comments
Collaborator

Verified against main at 61f42e6.

Both image paths set a one-year immutable cache header unconditionally:

  • internal/handlers/image.go:126
  • internal/handlers/imageenc.go:76
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")

But both URL schemes expire. A signed URL carries exp, and pixa returns 410/401 once it passes; an encrypted URL carries a TTL (the manual test pass recorded in TODO.md confirms a ttl=1 URL returns 410 Gone after 3 s).

So for any URL with a TTL shorter than a year — i.e. all of them — we instruct every intermediary cache and browser to keep serving the content for a year after we would refuse to serve it ourselves. Expiration becomes advisory the moment a CDN or corporate proxy is in front of pixa, which defeats the stated purpose of putting expiry in the signature ("to prevent replay attacks", README.md:69-70).

immutable compounds it: conforming clients will not revalidate even on a forced reload.

Definition of done

  1. max-age is clamped to the remaining lifetime of the URL (exp - now for signed URLs, remaining TTL for encrypted URLs), never exceeding it.
  2. Decide and document whether immutable is still appropriate; it is defensible for content-addressed variants within the URL's own lifetime, but state the reasoning in the PR.
  3. A URL with no expiry (allowlisted host, unsigned) may keep the long max-age — confirm that path still does.
  4. Failing tests first: a request through a signed URL expiring in 60 s must not emit a max-age greater than 60; same for a short-TTL encrypted URL.
  5. make check green.
Verified against `main` at `61f42e6`. Both image paths set a one-year immutable cache header unconditionally: - `internal/handlers/image.go:126` - `internal/handlers/imageenc.go:76` ```go w.Header().Set("Cache-Control", "public, max-age=31536000, immutable") ``` But both URL schemes expire. A signed URL carries `exp`, and pixa returns 410/401 once it passes; an encrypted URL carries a TTL (the manual test pass recorded in `TODO.md` confirms a `ttl=1` URL returns 410 Gone after 3 s). So for any URL with a TTL shorter than a year — i.e. all of them — we instruct every intermediary cache and browser to keep serving the content for a year after we would refuse to serve it ourselves. Expiration becomes advisory the moment a CDN or corporate proxy is in front of pixa, which defeats the stated purpose of putting expiry in the signature ("to prevent replay attacks", `README.md:69-70`). `immutable` compounds it: conforming clients will not revalidate even on a forced reload. ## Definition of done 1. `max-age` is clamped to the remaining lifetime of the URL (`exp - now` for signed URLs, remaining TTL for encrypted URLs), never exceeding it. 2. Decide and document whether `immutable` is still appropriate; it is defensible for content-addressed variants within the URL's own lifetime, but state the reasoning in the PR. 3. A URL with no expiry (allowlisted host, unsigned) may keep the long max-age — confirm that path still does. 4. Failing tests first: a request through a signed URL expiring in 60 s must not emit a `max-age` greater than 60; same for a short-TTL encrypted URL. 5. `make check` green.
clawbot added this to the 1.0.0 milestone 2026-08-09 03:43:32 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#63