OG Screenshot Worker
Generate dynamic Open Graph images using Cloudflare Browser Rendering.
This Cloudflare Worker takes live screenshots of pages on example.com and serves them as OG images for social media previews.
How It Works · Setup · Configuration · Usage · Cache Warmup
How It Works
Section titled “How It Works”When someone shares a link to example.com/plugins/wp-loupe/, social
platforms fetch https://og.example.com/plugins/wp-loupe.png. The worker
caches on two tiers and only renders on a full miss:
- Edge cache (Cache API) — Returns the image from the colo-local edge cache if present (fastest, no KV read)
- KV cache — On an edge miss, returns the screenshot from KV (durable, cross-colo) and warms the edge cache
- Launches headless browser — On a full miss, uses Cloudflare Browser Rendering
- Takes screenshot — Captures the page at 1200×630 pixels (OG standard)
- Populates both caches — Stores in KV for 7 days and in the edge cache
(non-blocking, via
waitUntil) - Returns PNG — Serves the image with proper headers
Prerequisites
Section titled “Prerequisites”- Cloudflare Workers Paid plan (required for Browser Rendering)
wranglerCLI installed
1. Create KV Namespace
Section titled “1. Create KV Namespace”cd og-screenshot-workernpx wrangler kv namespace create CACHECopy the returned id into wrangler.toml.
2. Deploy Worker
Section titled “2. Deploy Worker”npm installnpx wrangler deployFor local development:
npx wrangler dev3. Add DNS Record
Section titled “3. Add DNS Record”In Cloudflare Dashboard → DNS → Add record:
| Type | Name | Content | Proxy |
|---|---|---|---|
| AAAA | og | 100:: | Proxied |
This routes og.example.com to the worker.
4. Update Head Component
Section titled “4. Update Head Component”In your Astro site, set the og:image URL:
---const ogSlug = Astro.locals.starlightRoute?.id || slug || 'index';const ogImage = `https://og.example.com/${ogSlug}.png`;---
<meta property="og:image" content={ogImage} /><meta property="og:image:width" content="1200" /><meta property="og:image:height" content="630" />Configuration
Section titled “Configuration”wrangler.toml
Section titled “wrangler.toml”name = "og-screenshot"main = "src/index.ts"compatibility_date = "2025-01-01"compatibility_flags = ["nodejs_compat"]
[browser]binding = "BROWSER"
[[kv_namespaces]]binding = "CACHE"id = "your-kv-namespace-id"
[[routes]]pattern = "og.example.com/*"zone_name = "example.com"src/index.ts
Section titled “src/index.ts”src/index.ts contains the worker code that handles requests, checks caches,
and generates screenshots. It uses the Cloudflare Browser Rendering API to take
screenshots of pages and serves them as PNG images.
Source code is available at GitHub.
Environment Variables
Section titled “Environment Variables”| Binding | Type | Description |
|---|---|---|
BROWSER | Browser | Cloudflare Browser Rendering binding |
CACHE | KV Namespace | Screenshot cache storage |
URL Format
Section titled “URL Format”https://og.example.com/{slug}.pngExamples
Section titled “Examples”| Page | OG Image URL |
|---|---|
| Homepage | https://og.example.com/index.png |
| About | https://og.example.com/about.png |
| WP Loupe | https://og.example.com/plugins/wp-loupe.png |
| AI Router | https://og.example.com/ai/ai-router.png |
Cache Headers
Section titled “Cache Headers”X-Cache: HIT-EDGE— Served from the local Cloudflare edge cache (fastest path).X-Cache: HIT-KV— Served from Workers KV after an edge miss, then written back to edge cache.X-Cache: MISS— Fresh screenshot generated, then written to both KV and edge cache.
Testing
Section titled “Testing”# Check if workingcurl -I https://og.example.com/about.png
# Verify cache hit on second requestcurl -I https://og.example.com/about.png | grep -i X-CacheCache Warmup
Section titled “Cache Warmup”A GitHub Action runs weekly to warm the OG image cache, ensuring fast social media previews.
Automatic Warmup
Section titled “Automatic Warmup”The workflow runs every Sunday at 06:00 UTC:
name: Warm OG Image Cache
on: schedule: - cron: '0 6 * * 0' # Weekly on Sunday at 06:00 UTC workflow_dispatch:
jobs: warm-cache: runs-on: ubuntu-latest steps: - name: Fetch sitemap and warm OG cache run: | curl -s https://example.com/sitemap.xml | \ sed -n 's/.*<loc>https:\/\/example\.com\/\([^<]*\)<\/loc>.*/\1/p' | \ sed 's/\/$//' | \ while read slug; do [ -z "$slug" ] && slug="index" curl -s -o /dev/null \ "https://og.example.com/${slug}.png" doneManual Warmup
Section titled “Manual Warmup”Trigger the workflow manually from GitHub Actions, or run locally:
cat sitemap.xml | \ sed -n 's/.*<loc>https:\/\/example\.com\/\([^<]*\)<\/loc>.*/\1/p' | \ sed 's/\/$//' | \ while read slug; do [ -z "$slug" ] && slug="index" curl -s -o /dev/null -w "%{http_code} ${slug}\n" \ "https://og.example.com/${slug}.png" doneFallback Behavior
Section titled “Fallback Behavior”If screenshot fails (timeout, browser error), the worker falls back to the
static Satori-generated OG image at /og/{slug}.png.
Contributor Notes
Section titled “Contributor Notes”Implementation Contract
Section titled “Implementation Contract”The worker is easiest to maintain when these boundaries are kept explicit:
- Slug parsing and validation
- Cache lookup order (Edge, then KV)
- Rendering on full miss only
- Fallback fetch if rendering fails
If you refactor, preserve this order to avoid hidden cost increases in Browser Rendering usage.
Safe Refactor Checklist
Section titled “Safe Refactor Checklist”- Keep
X-Cachesemantics accurate (HIT-EDGE,HIT-KV,MISS) - Ensure browser instances are closed in success and failure paths
- Avoid changing cache key format without a migration strategy
- Validate slug parsing before constructing target URLs
Cost Considerations
Section titled “Cost Considerations”Browser Rendering is included in Workers Paid plan:
- First 1,000 browser sessions/month free
- $0.02 per additional session
With 7-day caching, typical documentation sites stay well within $5 tier.
AI Contribution Attribution
Section titled “AI Contribution Attribution”Assisted-by: GitHub Copilot:GPT-5.3-Codex
📦 Source: soderlind/og-screenshot-worker · Edit on GitHub