feat(workers): screenshot surfaces as PNG via /s/:id.png#126
Merged
Conversation
Add Browser Rendering support to the CF entrypoint. GET /s/:id.png screenshots the rendered surface page and returns a PNG image. Auth is fully delegated to the app — the user's credentials are forwarded to the DO, and the screenshot only proceeds if the app returns 200. The browser rendering call authenticates with the server's own token. - Add browser binding to wrangler.jsonc - Intercept .png requests in the entrypoint before DO dispatch - 1200×630 viewport (OG-image friendly) - Cache-Control: public, max-age=300 for edge caching
Forward ?theme= and ?mode= to the /s/:id page so screenshots match the viewer's rendering. Without mode pinning, headless Chrome's OS default produced mismatched colors. - Default width 800px (was 1200), configurable via ?w= (clamped 320–1920) - Mode defaults to light (headless Chrome's environment), ?mode=dark supported - Theme falls back to board setting; ?theme= overrides - Viewport height maintains 1200:630 aspect ratio
Use fullPage: true so the screenshot captures the entire surface content instead of clipping to a 1200:630 viewport. Tall surfaces were being cut off. The viewport height (800) is now just the initial window size.
Appending ?nocache to a .png URL bypasses the edge cache and returns Cache-Control: no-store, ensuring a fresh browser render.
Browser Rendering's quickAction cache (default 5s) keys on the URL path and ignores query params, so ?theme= overrides were returning stale screenshots. Set cacheTTL: 0 to disable it — we handle caching ourselves via Cache-Control headers.
The viewer now persists the resolved OS color-scheme in a sideshow_mode cookie. The .png screenshot handler reads it as the default mode, so screenshots match what the user sees without needing an explicit ?mode= param. Explicit ?mode= still overrides.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Append
.pngto any surface URL to get a screenshot:Uses Cloudflare Browser Rendering's
quickAction("screenshot")to render the surface page at 1200×630 and return a PNG.How it works
GET /s/:id.pngis intercepted in the Workers entrypoint (before the DO)/s/:id?part=0with the user's original headers/cookies — the app's auth middleware decides whether to allow itAuth is fully delegated to the app — the entrypoint has zero auth logic, so changes to auth rules are automatically respected.
Changes
wrangler.jsonc: addbrowserbindingworkers/index.ts: ~25 lines in the entrypoint to intercept.pngrequestsZero changes to
server/or the viewer.Details
Cache-Control: public, max-age=300for edge cachingnetworkidle0wait ensures JS-rendered content is capturedhtmlparts (the/s/:idroute only serves those)