Skip to main content
@vertz/cloudflare provides createHandler — a single function that wires up your API, SSR, security headers, and optional ISR caching into a Cloudflare Worker module.

Quick start

That’s it. The handler automatically:
  • Routes /api/* requests to your server (entities, auth, services)
  • Renders all other routes via SSR
  • Adds security headers with per-request nonce-based CSP
  • Detects requestHandler on ServerInstance for auth-aware routing

Configuration

CloudflareHandlerConfig

SSR module config

The zero-boilerplate form passes your app module directly:
For full control, pass a custom callback instead:

Route splitting

The handler splits requests by URL path:
RouteHandler
/api/*Server handler (entities, auth, services)
/_vertz/imageImage optimizer (if configured)
Everything elseSSR
Static assets (JS, CSS, images) are served by Cloudflare’s [assets] directive in wrangler.toml before the Worker runs — they never reach createHandler.

Custom apiPrefix

If your server uses a non-default apiPrefix, match it in the handler:
When apiPrefix is omitted from createHandler, it reads app.apiPrefix at runtime — so if you set it on createServer, the handler picks it up automatically.

Auth-aware routing

When your server uses auth (OAuth, sessions), createServer returns a ServerInstance with a requestHandler method that routes both auth and entity requests. The handler detects this automatically:
No manual wiring needed. /api/auth/signin, /api/auth/callback, etc. are all handled. If your app is a plain AppBuilder without auth, the handler falls back to app.handler automatically.

Security headers

Security headers are enabled by default. Every response includes:
HeaderValue
Content-Security-Policydefault-src 'self'; script-src 'self' 'nonce-<random>'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;
Strict-Transport-Securitymax-age=31536000; includeSubDomains
X-Content-Type-Optionsnosniff
X-Frame-OptionsDENY
X-XSS-Protection1; mode=block
Referrer-Policystrict-origin-when-cross-origin
Each request gets a unique cryptographic nonce for CSP. Script tags in SSR output use this nonce. To disable security headers (not recommended):

ISR caching

ISR (Incremental Static Regeneration) caches SSR responses in Cloudflare KV. This gives you near-static performance with dynamic content.

How it works

  1. First request — SSR renders the page, stores the HTML in KV, returns the response (X-Vertz-Cache: MISS)
  2. Subsequent requests (within TTL) — serves from KV instantly (X-Vertz-Cache: HIT)
  3. After TTL expires — serves the stale page immediately, re-renders in the background via ctx.waitUntil() (X-Vertz-Cache: STALE)

Setup

Add a KV namespace in wrangler.toml:
Configure the cache:

Cache behavior

  • Only SSR routes are cached — API requests (/api/*) are never cached
  • Nonces are stripped before caching and re-injected on each request (each response gets a fresh CSP nonce)
  • KV entries expire at 2x the TTL to allow stale-while-revalidate to work
  • KV lookup failures are non-fatal — the handler falls through to SSR

beforeRender middleware

Use beforeRender to run logic before SSR on non-API routes. Return a Response to short-circuit, or undefined to proceed with SSR:
beforeRender receives the Request and the Worker env bindings. It runs after API routing but before SSR and ISR cache checks.

Wrangler configuration

The [assets] directive serves static files (JS bundles, CSS, images) directly from Cloudflare’s edge — they never hit the Worker. Only dynamic requests (API calls, page navigations) reach createHandler.

Deploy

How it differs from static deployment

Static siteFull-stack (Workers)
ServerNone — static files onlyCloudflare Worker with SSR
DataBuild-time onlyPer-request (queries, auth)
PackageCustom build script@vertz/cloudflare
CachingEdge CDN (immutable assets)ISR via KV (optional)
Use caseLanding pages, marketingApps with dynamic data

Static Sites

Deploy static sites and landing pages

SSR

How Vertz SSR works under the hood