Edge Deployment

The /init endpoint determines consent policy from geo headers, resolves translations, and optionally fetches the GVL. None of this requires a database. The @c15t/backend/edge export lets you run this logic in edge runtimes (Vercel Middleware, Cloudflare Workers, Deno Deploy) so the consent banner resolves from the nearest PoP instead of round-tripping to your origin.

Info

The edge runtime exports in @c15t/backend/edge are unstable in 2.0. Use the unstable_-prefixed callables and expect API changes or removal in a future release.

When to use this

  • Your origin server is in a single region and users are globally distributed
  • You want the consent banner to appear as fast as possible (edge latency is typically 10-50ms vs 100-300ms to origin)
  • You already use edge middleware for other purposes (auth, redirects, A/B testing)

You do not need this if:

  • Your origin is already multi-region or on a platform like Cloudflare Workers
  • Consent banner latency is not a concern for your use case

Setup

1. Extract shared config

Keep your policy configuration in a shared file so both the edge handler and origin handler stay in sync:

2. Create edge middleware

3. Keep your origin handler unchanged

The origin API route still handles all database-dependent endpoints (/subjects, /consents, /status). The only difference is that /init requests no longer reach the origin — they're intercepted at the edge.

Configuration

unstable_c15tEdgeInit accepts C15TEdgeOptions — the same fields as c15tInstance minus the database-related options (adapter, tablePrefix, basePath, openapi, ipAddress, apiKeys, background).

OptionRequiredDescription
trustedOriginsYesAllowed CORS origins — must match your origin handler
policyPacksNoRegional policy configuration
policySnapshotNoSigning key for policy snapshot tokens
iabNoIAB TCF configuration
i18nNoTranslation profiles
brandingNoBanner branding (default: "c15t")
appNameNoApplication name (default: "c15t")
tenantIdNoTenant ID for multi-tenant deployments
cacheNoExternal cache adapter for GVL
disableGeoLocationNoDisable geo-location detection
telemetryNoOpenTelemetry configuration
loggerNoLogger configuration

Info

The edge handler and origin handler must share the same policyPacks, policySnapshot, trustedOrigins, iab, and i18n configuration. If they diverge, /init will return policies that don't match what the database endpoints expect. Use a shared config file as shown above.

How it works

The edge handler:

  1. Reads geo headers — Vercel sets x-vercel-ip-country and x-vercel-ip-country-region automatically. Cloudflare sets cf-ipcountry. The handler checks all common provider headers.
  2. Resolves jurisdiction — Maps the country/region to a jurisdiction code (GDPR, CCPA, UK_GDPR, etc.)
  3. Matches a policy pack — Finds the first matching policy for the visitor's location
  4. Resolves translations — Picks the right language from Accept-Language and your i18n config
  5. Signs a snapshot token — Creates a JWT proving which policy was served (if policySnapshot is configured)
  6. Handles CORS — Validates the Origin header against trustedOrigins and sets appropriate headers

All of this uses the same functions as the full c15tInstance — the edge handler is not a reimplementation, it's the same code without the database layer.

Caching considerations

Edge isolates have short-lived memory. The in-memory GVL cache resets on each cold start. For production:

  • Bundle GVL translations using iab.bundled to avoid fetch latency entirely
  • Use an external cache (Upstash Redis, Cloudflare KV) via the cache.adapter option to share cached data across isolates — see the Caching guide for setup

Info

Experimental — this API may change in future versions.

If you manage your own consent cookie and just need to know which categories to load for a given visitor, use unstable_resolveConsent instead of unstable_c15tEdgeInit. It's a lightweight, fully synchronous function that returns the matched policy and default consent state — no translations, GVL, branding, or snapshot tokens.

ModelnecessaryOther categoriesNotes
opt-ingranted, requirednot grantedUnless listed in preselectedCategories
opt-outgranted, requiredgrantedGPC signal can override marketing/measurement to not granted
nonegranted, requiredgrantedNo banner shown

unstable_resolveConsent vs unstable_c15tEdgeInit

unstable_resolveConsentunstable_c15tEdgeInit
Use caseCustom consent cookieDrop-in /init replacement
SyncYesNo (async — signs JWT, fetches GVL)
ReturnsPolicy + default consent stateFull /init JSON payload
CORSNot handled (your middleware)Built-in
TranslationsNot includedIncluded
Snapshot tokenNot includedIncluded

What stays on the origin

Only /init moves to the edge. These endpoints still require the origin server:

  • POST /subjects — creates/updates consent subjects (needs DB)
  • POST /consents — records consent decisions (needs DB)
  • GET /status — checks current consent status (needs DB)

The edge handler is a single-purpose optimization for the one endpoint that doesn't need persistent storage.