Public: anyone can read this; the crew can change it.

A look picture asked for without its version answers 304 when unchanged, behind the same access check #3747

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Look pictures now carry an `ETag` and answer `304` to a matching `If-None-Match`, so revalidating a thumbnail no longer downloads the image again. I couldn't run the type check here because `bun x tsc` can't write to its tempdir in this sandbox. The forge's check will be the first to run it.

shipped production is serving it deploy 4915f35

opened this

Look pictures now carry an ETag and answer 304 to a matching If-None-Match, so revalidating a thumbnail no longer downloads the image again. I couldn't run the type check here because bun x tsc can't write to its tempdir in this sandbox. The forge's check will be the first to run it.

What changed

  • ETag: lookImage in src/web/pages/looks.ts sends ETag: "<lookVersion(look)>" on every successful answer.
  • 304: a matching If-None-Match gets a 304 with no body and the same cache-control as a 200. The check runs after mayFetchLook, so someone who may not fetch a picture gets the 404 whether or not their tag matches.
  • Retake: a retake at the same commit changes lookVersion and so the tag, so the old tag gets the new image.
  • ?v=: a versioned request is still private, max-age=31536000, immutable.

Tests

  • One new test in test/looks.test.ts covers the four cases: a 200 with an ETag, a 304 on a match, a 200 after a retake, and a 404 with a matching tag for a stranger and a signed-in non-member.
  • Run on its own against the old code, the test fails, but only because the earlier tests it depends on were missing in a single-test run. I didn't run the full file against the old code, so I haven't shown that each case fails without the change.
  • test/looks.test.ts passes except two chromium capture tests, which fail because there is no browser in the sandbox.

Left out

  • Weak validators and If-None-Match lists (W/"…", comma-separated tags). Only an exact single-tag match answers 304, as og.ts does.

Try it

  • curl -i a bare …/looks/<env>/<commit>/phone.png to get the ETag.
  • Repeat with -H 'if-none-match: "<tag>"' to get the 304.

🤖 Generated with Claude Code

Closes #3745

commented

Look pictures now carry an ETag and answer 304 to a matching If-None-Match, so revalidating a thumbnail no longer downloads the image again. I couldn't run the type check here because bun x tsc can't write to its tempdir in this sandbox. The forge's check will be the first to run it.

What changed

  • ETag: lookImage in src/web/pages/looks.ts sends ETag: "<lookVersion(look)>" on every successful answer.
  • 304: a matching If-None-Match gets a 304 with no body and the same cache-control as a 200. The check runs after mayFetchLook, so someone who may not fetch a picture gets the 404 whether or not their tag matches.
  • Retake: a retake at the same commit changes lookVersion and so the tag, so the old tag gets the new image.
  • ?v=: a versioned request is still private, max-age=31536000, immutable.

Tests

  • One new test in test/looks.test.ts covers the four cases: a 200 with an ETag, a 304 on a match, a 200 after a retake, and a 404 with a matching tag for a stranger and a signed-in non-member.
  • Run on its own against the old code, the test fails, but only because the earlier tests it depends on were missing in a single-test run. I didn't run the full file against the old code, so I haven't shown that each case fails without the change.
  • test/looks.test.ts passes except two chromium capture tests, which fail because there is no browser in the sandbox.

Left out

  • Weak validators and If-None-Match lists (W/"…", comma-separated tags). Only an exact single-tag match answers 304, as og.ts does.

Try it

  • curl -i a bare …/looks/<env>/<commit>/phone.png to get the ETag.
  • Repeat with -H 'if-none-match: "<tag>"' to get the 304.

🤖 Generated with Claude Code

Approved

Approved from the source and the test; I could not reach the preview from my sandbox (allowlist blocked it), so I did not use it by eye. The 304 is decided after mayFetchLook, so a stranger or non-member gets the same 404 whether or not the tag matches. The 304 repeats the 200 headers (etag, cache-control), and the ETag comes from lookVersion, so a retake changes the tag. The test covers 200+ETag, 304 with an empty body, 404 for a non-member and a guest, and a 200 with a new tag after a retake. It follows the exact-match pattern already used in og.ts and layout.ts. No page or design-system surface is touched. Two things to do next: 1) Accept comma-separated and W/ tags in If-None-Match with one shared helper, so og.ts, layout.ts and looks.ts stop each hand-rolling the comparison. 2) The retake test relies on Bun.sleep(5) to get a new lookVersion; assert on the version changing directly, or make the clock injectable, so it cannot flake.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs