OG Screenshot Worker
Generate dynamic Open Graph images using Cloudflare Browser Rendering.
This Cloudflare Worker takes live screenshots of pages on soderlind.no 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 soderlind.no/plugins/wp-loupe/, social platforms fetch https://og.soderlind.no/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 workers/og-screenshotnpx wrangler kv namespace create CACHECopy the returned id into wrangler.toml.
2. Deploy Worker
Section titled “2. Deploy Worker”npm installnpx wrangler deploy3. 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.soderlind.no 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.soderlind.no/${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.soderlind.no/*"zone_name = "soderlind.no"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.soderlind.no/{slug}.pngExamples
Section titled “Examples”| Page | OG Image URL |
|---|---|
| Homepage | https://og.soderlind.no/index.png |
| About | https://og.soderlind.no/about.png |
| WP Loupe | https://og.soderlind.no/plugins/wp-loupe.png |
| AI Router | https://og.soderlind.no/ai/ai-router.png |
Cache Headers
Section titled “Cache Headers”X-Cache: HIT— Served from cache (edge Cache API or KV). Cloudflare’scf-cache-statusdistinguishes an edgeHITfrom aMISSthat was served by the worker from KV.X-Cache: MISS— Fresh screenshot generated, then written to both KV and the edge cache.
Testing
Section titled “Testing”# Check if workingcurl -I https://og.soderlind.no/about.png
# Verify cache hit on second requestcurl -I https://og.soderlind.no/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://soderlind.no/sitemap.xml | \ sed -n 's/.*<loc>https:\/\/soderlind\.no\/\([^<]*\)<\/loc>.*/\1/p' | \ sed 's/\/$//' | \ while read slug; do [ -z "$slug" ] && slug="index" curl -s -o /dev/null "https://og.soderlind.no/${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:\/\/soderlind\.no\/\([^<]*\)<\/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.soderlind.no/${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.
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.
📦 Source: soderlind/og-screenshot-worker · Edit on GitHub