NEWS

Cloudflare Workers gets gateway pattern and feature workers to avoid a monolith at the edge

A technical report published on InfoQ describes how to split edge logic into specialized workers connected by service bindings, without paying a network hop, so that a deploy doesn't become an incident for every tenant.

Cloudflare Workers gets gateway pattern and feature workers to avoid a monolith at the edge
Image: Redação iMasters

The monolith born at the edge

Every edge computing project starts the same way: a Cloudflare Worker, a fetch handler, a route. Easy to understand, easy to deploy. The problem, according to Chintan Tank's report published on InfoQ on September 23, 2026 (reviewed by Renato Losio), is that this worker doesn't stay small for long when it sits in front of a SaaS with hundreds of thousands of tenant accounts. It keeps accumulating responsibilities: image optimization, failover pages, routing, header and cookie rewriting, per-tenant configuration lookup. Each of these responsibilities belongs, in practice, to a different team, but all of them live in the same file.

The side effect, for those building multi-tenant SaaS, is worse than a common application monolith. A worker isn't a backend service with replicas behind a load balancer: it is the request path itself. An unhandled exception, a bad regex, or a heavy loop in one feature doesn't just degrade that feature, it degrades every feature and every tenant at the same time. Deploys also become a problem: with a single worker there's only one single deploy, so a one-line header fix waits behind a half-finished experiment, because the two ship together.

Gateway and feature workers: splitting without paying the network price

The obvious way out of a monolith is to split it, but splitting has historically cost one extra network call per extracted service (DNS, TLS handshake, round trip). At the edge, where the goal is to save milliseconds, that cost usually makes splitting unviable. On Cloudflare Workers that doesn't hold, because of service bindings: a worker-to-worker call that Cloudflare dispatches within the same isolate, on the same machine. The call looks like a fetch() but costs like a local function call.

This property is what underpins the pattern described in the article: a thin gateway worker in front of several single-purpose feature workers. The gateway does only two things: it decides which features apply to the request (and in what order) and it handles cross-cutting concerns like request preparation, header hygiene, and observability. It never knows the internals of each feature. The contract is split into two parts: a cheap predicate of the shouldApply() type, which the gateway runs inline at no network cost, and the worker itself, dispatched via service binding only when the predicate says the work is necessary. In code:

Architecture diagram shows the gateway worker receiving the client request and routing via service binding to the failover/maintenance and image optimization workers, before fetching the origin
Architecture diagram shows the gateway worker receiving the client request and routing via service binding to the failover/maintenance and image optimization workers, before fetching the origin. Reprodução: infoq.com.
ts
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    if ((await shouldServeFailover(request, env)).serve) return env.FAILOVER.fetch(request);
    const originRequest = prepareOriginRequest(request, env);
    const response = (await shouldOptimize(originRequest, env)).optimize
      ? await env.IMAGE_OPTIMIZER.fetch(originRequest)
      : await fetchFromOriginOrCache(originRequest, ctx);
    return shouldServeFailoverForResponse(request, response).serve
      ? env.FAILOVER.fetch(request)
      : sanitize(response);
  },
};

Each feature worker carries its own dependencies and its own CPU budget, without bloating the gateway's bundle, which matters because Workers run under a fixed ceiling for CPU per request and script size. And if a feature fails or times out, the gateway serves the original response from origin instead of an error page: resilience lives in both layers, and neither one brings down the entire request by itself.

The price of independence: monorepo and fragmented tracing

Separating workers isn't free, the price is coordination. The team described in the article put the gateway and every feature worker in a single monorepo, with package boundaries ensuring that each worker remains an independent build and deploy target, and each feature's external facade being the only surface the gateway is allowed to import. It's the middle ground between the isolation of separate services and the coherence of a single codebase: without it, the shared contract drifts and a tweak to the gateway's interface turns into a multi-repository migration.

The split also shatters observability. A monolithic worker sees the entire request; a chain of workers separated by service binding, each one sees only its own slice. The report describes using a special header that carries diagnostics in the response itself to track a request in flight without relying on centralized logging, but it works at the gateway and doesn't cross the binding: diagnostics generated inside the image feature worker, for instance, simply don't come back. Tracing a request end to end is work the monolith gave for free and that modular architecture requires rebuilding.

Cloudflare isn't Akamai, and porting isn't copying

The article is emphatic on a point that matters directly to whoever decides the stack: the gateway and feature workers pattern is a Cloudflare architecture, and it doesn't automatically port to any CDN with serverless compute. The author implemented the same image optimization feature on Akamai and Cloudflare and documented the difference.

On Cloudflare, the unit of computation is the worker: a single fetch handler that owns the request from start to finish, can call other workers via binding, fetch the origin, and rewrite the response. On Akamai, the unit of configuration is the property, a rules engine that matches request attributes and applies behaviors; the EdgeWorker is a guest inside that pipeline, invoked at named lifecycle events, without owning the request. In practice, Akamai's opt-out EdgeWorker doesn't call Image Manager (Akamai's managed image optimization product) and doesn't transform anything: it only writes a decision variable that a property rule reads afterward.

js
// Akamai EdgeWorker: writes decision, doesn't execute the transformation
export async function onClientRequest(request) {
  const key = deriveSiteKey(request);
  const optedOut = await lookupOptOut(request, key);
  request.setVariable("PMUSER_SKIP_TRANSFORM", optedOut ? "true" : "false");
}

The difference goes all the way down to the data layer. Workers KV, Cloudflare's key-value store, is global: a write is readable anywhere. Akamai's EdgeKV, at the time the team built this version, was provisioned per region, with no single namespace covering everything, which forced the worker to map the request's continent of origin to a regional store before even doing the read. The article notes that Akamai later launched a global namespace, which shows that parity between platforms isn't a state you reach and keep: it decomposes in both directions as each vendor evolves its product.

This detail matters beyond technical curiosity. If the business requirement is global reach, an automatically global store is the simplest piece and regional routing becomes a tax. But flip the requirement, a data residency rule that keeps data from one region inside that region, and the pieces swap places: Akamai's per-region namespace becomes the feature, and Cloudflare's global Workers KV starts requiring you to reach for another primitive instead of a ready-made configuration. For those building SaaS in Brazil under LGPD (Brazil's data protection law) requirements involving data retention or localization, this is a question that the choice of edge platform doesn't answer by itself: it needs to be designed case by case.

Image optimization as a build-versus-buy case study

The article uses image optimization to illustrate the build-versus-buy decision every edge feature faces. On Akamai, optimization is a managed service (Image Manager): you configure a policy and it negotiates each request with no code. On Cloudflare, it's a lower-level primitive: the worker specifies the transformation per request. Cloudflare has an equivalent managed product, Polish, but it operates through the cache, matching URLs by file extension and skipping anything that isn't publicly cacheable, which didn't work for image traffic that isn't uniformly extensioned nor uniformly public.

The choice of code over a toggle produced three decisions that the worker applies in order: first, security (never optimize an image that carries any authentication signal, because optimizing means caching at a shared edge, and a private image cached by URL leaks to the next request without going through the origin that would check authorization); second, format negotiation via the Accept header (the article cites independent benchmarks that put AVIF at roughly half the size of an equivalent JPEG and WebP about a third smaller, but serving AVIF to a client that can't decode it is worse than not optimizing at all); third, sizing by device class read from request signals.

What changes for those building SaaS in Brazil

For Brazilian teams already running Cloudflare Workers in production, or evaluating a move of edge logic there, the article's practical takeaway is this: the gateway-plus-feature-workers pattern only pays off past a scale where a single worker has become a bottleneck for deploys and blast radius, not before. Small or single-tenant companies don't feel that pain and can keep a simple worker without issue. The conversation changes once the product starts serving multiple isolated tenants with different feature teams fighting over the same file.

Two points deserve attention from anyone planning to replicate this. First, service bindings are the piece that makes the split worth it without a network hop: without this Cloudflare-specific feature, the same design on another edge platform can cost real latency, and the article shows this by comparing directly with Akamai, where there's no equivalent binding between EdgeWorker and Image Manager. Second, testing edge code demands the same discipline as conventional applications: a unit test suite per feature worker, integration testing at the gateway, synthetic monitoring in production, because without that the deploy independence the architecture promises turns into the independence to break your neighbor without warning.

The report itself leaves open the cost of observability: modular architecture solves deployment and blast radius, but fragments the tracing of a request that crosses multiple workers, and the article doesn't describe a definitive solution for this, only the stopgap of the diagnostic header that doesn't cross bindings.

Translated from the Brazilian Portuguese original · Read the original