Skip to content

Cloudflare

Beam draait 100% op Cloudflare — hosting, API runtime, media opslag, image processing en custom domains. Geen AWS, geen Vercel, geen VPS.

ServiceGebruik
PagesDashboard + public site hosting
WorkersAPI + router worker + cron triggers
R2Media opslag (originals + thumbnails)
Image ResizingBlur placeholders, thumbnails, responsive images
Custom Domains APIKlant-domeinen koppelen
Cache APIPagina cache + purge bij publicatie

Twee Pages-projecten:

ProjectDomeinApp
beam-dashboardapp.builtwithbeam.comVite SPA (dashboard)
beam-public-siteKlant-domeinenAstro SSR (publieke sites)

Deploy via GitHub Actions (wrangler pages deploy). Dashboard build uploadt ook Sentry source maps.

Twee Workers:

Hono 4.7 op api.builtwithbeam.com. Alle CRUD operaties, media uploads, team management.

Cron triggers:

  • */5 * * * * — Domain status polling (pending → active/error)
  • 0 3 * * * — Media cleanup (orphaned bestanden, max 100 per run)

Routeert *.builtwithbeam.com traffic:

PatroonDestination
app.builtwithbeam.com, www.builtwithbeam.comDashboard Pages project
docs.builtwithbeam.comDocs Pages project
Alle andere subdomainsPublic site Pages project

De worker set Host naar het origin en X-Beam-Host naar het oorspronkelijke hostname.

Bucket: MEDIA_BUCKET (binding in wrangler.toml)

{siteId}/
originals/{fileId}.{ext} # Origineel bestand
thumbnails/{fileId}.webp # 400px WebP thumbnail
  • Direct binding (geen S3 API overhead)
  • Geen egress kosten
  • Cleanup cron verwijdert orphaned bestanden dagelijks

Gebruikt voor drie doelen:

DoelFormaatBreedte
Blur placeholder (LQIP)Base64 data URL20px
ThumbnailWebP400px
Responsive afbeeldingenOrigineel formaat640, 750, 828, 1080, 1200, 1920, 2048, 3840px

URL patroon: https://domain.com/cdn-cgi/image/width=W,quality=Q,format=auto/path

Publieke site gebruikt srcSet met alle breedtes voor optimale afbeeldingen per viewport.

Flow:

Gebruiker voert domein in
→ POST /domains/connect
→ Validatie: DNS regex, blocked list, max 5 per site
→ CF Pages Custom Domain aanmaken via CF API
→ Record in site_domains (status: pending)
→ Cron */5 min: poll CF API
→ DNS geconfigureerd? → status: active
→ 48 uur geen respons? → status: error

API: CF Pages Custom Domains API (niet de legacy Custom Hostnames API).

Limieten:

  • Max 5 domeinen per site
  • Eerste domein wordt automatisch primary
  • Verwijderen: CF API cleanup + DB cleanup + next domain promoted

Strategie: s-maxage=300, stale-while-revalidate=600 op publieke pagina’s.

Purge: POST /cache/purge — max 30 URLs per request. Fire-and-forget bij pagina save.

Headers: Vary: Host voor correcte CDN caching per klant-domein.

VariabeleTypeDoel
CF_ACCOUNT_IDVarAccount identifier
CF_ZONE_IDVarZone identifier
CF_API_TOKENSecretAPI token (domains, cache)
DASHBOARD_ORIGINVarCORS allowed origin
MEDIA_BUCKETR2 bindingObject storage

Beam valt ruim binnen de gratis tiers:

ServiceFree tierBeam gebruik
Workers100K req/dagLaag
Pages500 builds/maand~30 deploys/maand
R210 GB opslag, 10M readsGroeit per site
Image ResizingBetaald ($0.50/1000)Per afbeelding
ProbleemOorzaakOplossing
Custom domain blijft “pending”DNS niet geconfigureerdCNAME naar Pages project instellen
Cache niet geïnvalideerdPurge mislukt of Vary: Host mistCheck POST /cache/purge response
R2 upload faaltBinding niet geconfigureerdCheck MEDIA_BUCKET in wrangler.toml
Image Resizing 403Feature niet enabled op zoneEnable in CF dashboard > Speed > Optimization