Inside the Gateway evaluation path

Two entry points let a Gateway policy call a Worker, and they occupy different positions in the enforcement flow. The Forward action is in public beta; Custom Functions are due before the end of the year.

Forward: proxy-style transformation

Policies carrying the Forward action are evaluated against traffic that HTTP policies allow. The first matching rule sends the request to its bound Worker, which acts as inline middleware with access to the full headers and body. That logic can rewrite either, forward to the origin, modify the response, and return the result — or take an asynchronous action such as messaging Slack, writing to R2, logging to an external pipeline, calling a webhook, or updating a KV store while the request continues untouched to its destination.

import { WorkerEntrypoint } from "cloudflare:workers";
import type {
  GatewayMiddlewareContext,
  GatewayMiddlewareNext,
} from "@cloudflare/zt-types";

export class GatewayMiddleware extends WorkerEntrypoint {
  async handle(
    request: Request,
    context: GatewayMiddlewareContext,
    next: GatewayMiddlewareNext,
  ): Promise<Response> {
    // Read a value from the Gateway context
    const clientIP = context.srcIp;

    // Add headers before the request reaches the origin
    request.headers.set("X-Gateway-Client-IP", clientIP);

    // Forward upstream and get the response
    const response = await next(request);

    // Modify the response before it reaches the user
    response.headers.set("X-Processed-By", "gateway-middleware");

    return response;
  }
}

Gateway owns the transport concerns: TCP connections, TLS termination and re-encryption, certificate management, and HTTP version negotiation. Only the transformation logic lives in the Worker, which gives you a proxy-like extension point without operating a proxy.

Custom Functions: values during evaluation

A Custom Function invokes a Worker inline during policy evaluation and receives a typed value back — string, number, or boolean. That value is then exposed as a selector in the policy builder and used in the condition of any network or HTTP rule, alongside built-ins such as Source IP or Content Category. The Worker gets request context including source IP, destination, user identity, device posture, and HTTP headers, varying by policy phase. The contract is data in, value out.

export class GatewayCustomSelector extends WorkerEntrypoint {
  async compute(ctx: GatewayContext): Promise<string> {
    const authHeader = ctx.request.headers.get("Authorization");
    if (!authHeader) return "anonymous";

    const token = authHeader.replace("Bearer ", "");
    const payload = JSON.parse(atob(token.split(".")[1]));
    return payload.groups?.[0] ?? "unknown";
  }
}

Once that function is deployed, a rule condition reads Custom Function "JWT Role" contains "finance_contractor" with an Allow action.

Because Custom Functions consume identity, device posture, and connection metadata rather than the HTTP request, they also work in the Gateway Network policy rule builder against arbitrary Layer 4 ports and protocols. Internally a Worker can call external APIs, read KV or D1, or invoke Workers AI, but from Gateway's side the evaluation stays deterministic.

Choosing between them

Custom Functions suit quick, bounded, stateless evaluations of data that return a shaped response — closer in spirit to existing selectors such as a port range or a URL path regex, extended with arbitrary or internal data. The Forward action needs more planning: it manipulates live requests on both the egress and the return trip, so its Workers should be targeted and designed carefully to avoid harming the end-user experience.

Patterns this unlocks

  • An interstitial page served by a Worker on requests to sensitive destinations, prompting for written justification. A session with a TTL written to Cloudflare KV means the user is asked once per configured period; the identity and justification go to Cloudflare D1 for an audit trail, or to an internal database via Hyperdrive. This was one of the most-requested features for over four years.
  • A Custom Function parses the JWT in the Authorization header and returns role or group membership, letting a rule set an X-App-Role header upstream — replacing a dedicated NGINX proxy pool.
  • A Worker triggered on file downloads can detonate the file in a third-party sandbox, or write it in full to Cloudflare R2 as a permanent record without it leaving the Cloudflare network.
  • A Worker bound to a Forward policy fires when a DLP profile matches and notifies Slack or PagerDuty with the user's email, the rule that fired, and the destination URL.
  • export default {
      async invoke(ctx: GatewayContext, env: Env): Promise<void> {
        await fetch(env.SLACK_WEBHOOK_URL, {
          method: "POST",
          body: JSON.stringify({
            text: `DLP alert: ${ctx.identity.email} triggered "${ctx.rule.name}" accessing ${ctx.request.url}`
          })
        });
      }
    }
  • Internal risk scoring or complex user/group maps that require multiple independently true criteria can be reduced by a Custom Function to a single value returned to Gateway.

Latency and failure handling

Gateway and Workers run on the same machines, so a dispatch to a Worker is a local function call with no network hop; added time comes only from the Worker's own execution. Each Custom Function has configurable fallback behavior for exceptions, timeouts, or unexpected values, and default values are required in the contract so that behavior is unambiguous.

Release status and billing

The Forward action and its Workers integration enter public beta today. Custom Functions are expected to ship before the end of the year. At general availability, both will be offered to all customers on every plan type.

Requests sent to your Workers are billed as standard Worker requests. Functionality is identical across Workers Free, Workers Paid, and Workers for Enterprise; the only caveat is that Workers Free carries execution time and daily request limits, which matters as you grow the volume of Gateway traffic routed to Workers.

Custom validation for DLP

Programmability extends past Gateway into the DLP product, which already spans Gateway, AI Gateway, Email Security and other surfaces. DLP now accepts custom outcomes through integrations with the Compute platform.

Pattern-based detection — regular expressions, word lists, keywords, context analysis on the request body — answers only whether data resembles a pattern relevant to your organization. It cannot tell you whether a given instance of that data signals exfiltration or misuse. As attackers grow more capable and agents are used to exploit systems at scale, better pattern matching is not sufficient; accuracy depends on knowing whether the data in question is actively relevant to you.

Exact data match is the usual remedy, but it forces customers to prepare, maintain and transfer reference datasets, and to accept a vendor managing data drawn from their most sensitive systems. Custom validation functions close that gap. They let customers encode their own validation logic against their own source of truth inside a Cloudflare Worker, and apply it everywhere DLP is used to protect sensitive data. They are available starting today.

Checking whether a credential is live

When a DLP detection entry matches, an outbound Worker can be dispatched to run validation logic before a policy enforcement decision is made. A profile that detects GitHub secrets, for example, can dispatch a Worker that looks up the status of the detected key, returns a response from the appropriate GitHub endpoint, and lets you decide whether the DLP profile should match the traffic.

import {
  DlpValidation,
  type DlpValidationInput,
  type DlpValidationResult,
} from "@cloudflare/zt-types";

export class DlpValidationEntrypoint extends DlpValidation {
  async validate({ excerpt, start, end }: DlpValidationInput): Promise<DlpValidationResult> {
    const bytes = Uint8Array.from(atob(excerpt), (c) => c.charCodeAt(0));
    const token = new TextDecoder().decode(bytes.slice(start, end));

    const res = await fetch("https://api.GitHub.com/user", {
      headers: {
        Authorization: `Bearer ${token}`,
        "User-Agent": "dlp-GitHub-validator",
        Accept: "application/vnd.GitHub+json",
      },
    });

    if (res.ok) return { score: 1.0 }; // token is valid and active
    if (res.status === 401) return { score: "veto" }; // token is invalid, discard
    return { score: 0 };
  }
}

Where this is heading

This is the first step toward a fully programmable SASE platform. Work is underway to expand the entry points from which custom functionality can be dispatched across the entire Cloudflare One suite, with Email Security next. The architecture also makes it easier to build first-party features into all Cloudflare One services faster and with a better developer experience.

Protecting Workers, agents and their traffic with Cloudflare One is likewise becoming easier. Enterprise customers who want an implementation strategy session, a preview of upcoming functionality, or notification about the custom functions beta can contact their account team.

Organizational security can now do anything a Cloudflare Worker can do.