You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a GitHub-embedded image points at a file that has been deleted, GitHub renders a broken-image icon. For images we can detect are being fetched by GitHub's Camo proxy, we could instead serve a friendly "this image may have been deleted" placeholder so the thread degrades gracefully.
Why this is low priority (deferred deliberately)
Cosmetic, rare trigger. The only case this improves is a genuinely-deleted file — uncommon for an internal tool. The payoff is replacing a broken-link icon with a nicer graphic.
The expensive part is already solved elsewhere. This started as a bigger investigation because a friendly 404 requires putting code in the request path, which dragged in cache-freshness concerns. Those are now handled at the source by fix(api): set Cache-Control: max-age=60 on uploads so overwrites propagate #10 (Cache-Control: max-age=60 on upload + purge-on-overwrite). So the freshness/caching complexity is out of scope for this — nothing here needs to touch cache behavior.
Design we landed on (if/when we build it)
Serving today: storage.uploads.sh is a direct R2 public custom domain — no Worker in the path — so R2 returns its stock XML 404 and we can't intercept it. Rules/Page Rules/Transform Rules cannot synthesize a placeholder body conditioned on an origin 404, and Custom Error Pages only cover Cloudflare-generated errors. So a friendly image genuinely requires code in the path.
To avoid fronting all reads with a Worker (and the cost/complexity that implies), use an alias domain instead of remapping storage.uploads.sh:
New tiny apps/cdn Worker on cdn.uploads.sh, bound to UPLOADS_DEFAULT only (no KV, no auth — the path after the host is the R2 key). Fronts only the shared uploads-default bucket.
storage.uploads.sh stays untouched — direct R2, free, zero Worker calls for normal/browser reads.
CLI emits the proxy host only for GitHub embeds. The --pr/--issue/--comment flow already knows it's producing a GitHub embed; add a toGithubEmbedUrl(url) helper that swaps host storage.uploads.sh → cdn.uploads.shonly when the host is storage.uploads.sh (BYO-bucket workspaces on their own publicBaseUrl no-op through it). Apply it in the put markdown, the managed comment body (attachmentsCommentBody), comment, and list --pr paths.
Worker request flow (GET/HEAD):
key = path minus leading /; reuse the API's badKey guard.
UPLOADS_DEFAULT.get(key) → hit: stream with Content-Type (from R2 httpMetadata), ETag, honor If-None-Match → 304. Cache-Control can just mirror what the API now sets.
miss: if User-Agent contains github-camo (case-insensitive) → static placeholder PNG at 200 (PNG, not SVG, for reliable Camo rendering; committed to apps/cdn and bundled so it can't itself 404), short Cache-Control so a later re-upload recovers on its own. Otherwise → plain 404 (matches today's behavior for browsers/hotlinks).
Scope / non-goals
Only the shared uploads-default bucket. BYO-bucket GitHub embeds serve from their own domain and won't get the placeholder — acceptable.
No Range requests, no dynamic/branded placeholder, no KV/workspace resolution in v1.
Cost note
Only GitHub-embedded reads ever hit the Worker; everything else stays on the free direct path. Even those are Camo-cached, so invocations are low. Workers free tier (100k/day) covers this comfortably.
Idea
When a GitHub-embedded image points at a file that has been deleted, GitHub renders a broken-image icon. For images we can detect are being fetched by GitHub's Camo proxy, we could instead serve a friendly "this image may have been deleted" placeholder so the thread degrades gracefully.
Why this is low priority (deferred deliberately)
Cache-Control: max-age=60on upload + purge-on-overwrite). So the freshness/caching complexity is out of scope for this — nothing here needs to touch cache behavior.Design we landed on (if/when we build it)
Serving today:
storage.uploads.shis a direct R2 public custom domain — no Worker in the path — so R2 returns its stock XML 404 and we can't intercept it. Rules/Page Rules/Transform Rules cannot synthesize a placeholder body conditioned on an origin 404, and Custom Error Pages only cover Cloudflare-generated errors. So a friendly image genuinely requires code in the path.To avoid fronting all reads with a Worker (and the cost/complexity that implies), use an alias domain instead of remapping
storage.uploads.sh:apps/cdnWorker oncdn.uploads.sh, bound toUPLOADS_DEFAULTonly (no KV, no auth — the path after the host is the R2 key). Fronts only the shareduploads-defaultbucket.storage.uploads.shstays untouched — direct R2, free, zero Worker calls for normal/browser reads.--pr/--issue/--commentflow already knows it's producing a GitHub embed; add atoGithubEmbedUrl(url)helper that swaps hoststorage.uploads.sh→cdn.uploads.shonly when the host isstorage.uploads.sh(BYO-bucket workspaces on their ownpublicBaseUrlno-op through it). Apply it in theputmarkdown, the managed comment body (attachmentsCommentBody),comment, andlist --prpaths.Worker request flow (
GET/HEAD):/; reuse the API'sbadKeyguard.UPLOADS_DEFAULT.get(key)→ hit: stream withContent-Type(from R2httpMetadata),ETag, honorIf-None-Match→304. Cache-Control can just mirror what the API now sets.User-Agentcontainsgithub-camo(case-insensitive) → static placeholder PNG at200(PNG, not SVG, for reliable Camo rendering; committed toapps/cdnand bundled so it can't itself 404), shortCache-Controlso a later re-upload recovers on its own. Otherwise → plain404(matches today's behavior for browsers/hotlinks).Scope / non-goals
uploads-defaultbucket. BYO-bucket GitHub embeds serve from their own domain and won't get the placeholder — acceptable.Cost note
Only GitHub-embedded reads ever hit the Worker; everything else stays on the free direct path. Even those are Camo-cached, so invocations are low. Workers free tier (100k/day) covers this comfortably.