---
title: Egress control
description: Restrict, intercept, and audit outbound network access for dynamic Workers.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/dynamic-workers/llms.txt  
> Use this file to discover all available pages before exploring further.

[Skip to content](#%5Ftop) 

# Egress control

When you run untrusted or AI-generated code in a dynamic Worker, you need to control what it can access on the network. You might want to:

* block all outbound access so the dynamic Worker can only use the [bindings](https://developers.cloudflare.com/dynamic-workers/usage/bindings/) you give it
* restrict outbound requests to a specific set of allowed destinations
* inject credentials into outbound requests without exposing secrets to the dynamic Worker
* log or audit every outbound request for observability

The `globalOutbound` option in the `WorkerCode` object returned by `get()` or passed to `load()` controls all of this. It intercepts every `fetch()` and `connect()` call the dynamic Worker makes.

## Block all outbound access

Set `globalOutbound` to `null` to fully isolate the dynamic Worker from the network:

JavaScript

```

return {

  mainModule: "index.js",

  modules: { "index.js": code },

  globalOutbound: null,

};


```

This causes any `fetch()` or `connect()` request from the dynamic Worker to throw an exception.

In this mode, you can still give the Dynamic Worker direct access to specific resources and services using [bindings](https://developers.cloudflare.com/dynamic-workers/usage/bindings/). This is the cleanest and most secure way to design your sandbox: block the Internet, then constructively offer specific capabilities via bindings.

That said, if you need to offer compatibility with existing HTTP client libraries running directly inside your Dynamic Worker sandbox, then blocking `fetch()` may be infeasible, and you may prefer to intercept requests instead.

## Intercept outbound requests

To intercept outbound requests, define a `WorkerEntrypoint` class in the loader Worker that acts as a gateway. Every `fetch()` and `connect()` call the dynamic Worker makes goes through this gateway instead of hitting the network directly. Pass the gateway to the dynamic Worker with `globalOutbound` and `ctx.exports`:

JavaScript

```

import { WorkerEntrypoint } from "cloudflare:workers";


export class HttpGateway extends WorkerEntrypoint {

  async fetch(request) {

    // Every outbound fetch() from the dynamic Worker arrives here.

    // Inspect, modify, block, or forward the request.

    return fetch(request);

  }

}


export default {

  async fetch(request, env, ctx) {

    const worker = env.LOADER.get("my-worker", async () => {

      return {

        compatibilityDate: "$today",

        mainModule: "index.js",

        modules: { "index.js": code },


        // Pass the gateway as a service binding.

        // The dynamic Worker's fetch() and connect() calls

        // are routed through HttpGateway instead of going

        // to the network directly.

        globalOutbound: ctx.exports.HttpGateway(),

      };

    });


    return worker.getEntrypoint().fetch(request);

  },

};


```

From here, you can add any logic to the gateway, such as restricting destinations, injecting credentials, or logging requests.

## Inject credentials

A common pattern is attaching credentials to outbound requests so the dynamic Worker never sees the secret. Similar to [custom bindings](https://developers.cloudflare.com/dynamic-workers/usage/bindings/#custom-bindings-with-dynamic-workers), you can use [ctx.props](https://developers.cloudflare.com/workers/runtime-apis/context/#props) to pass per-tenant or per-request context to the gateway.

The dynamic Worker calls `fetch()` normally. `HttpGateway` intercepts the request, attaches the token from the loader Worker's environment, and forwards it. The dynamic Worker never has access to `API_TOKEN`.

JavaScript

```

import { WorkerEntrypoint } from "cloudflare:workers";


export class HttpGateway extends WorkerEntrypoint {

  async fetch(request) {

    let url = new URL(request.url);

    const headers = new Headers(request.headers);


    // For requests to api.example.com, inject credentials.

    if (url.hostname === "api.example.com") {

      headers.set("Authorization", `Bearer ${this.env.API_TOKEN}`);

      headers.set("X-Tenant-Id", this.ctx.props.tenantId);

    }


    return fetch(request, { headers });

  }

}


export default {

  async fetch(request, env, ctx) {

    const tenantId = getTenantFromRequest(request);


    const worker = env.LOADER.get(`tenant:${tenantId}`, async () => {

      return {

        mainModule: "index.js",

        modules: {

          "index.js": `

            export default {

              async fetch() {

                const resp = await fetch("https://api.example.com/data");

                return new Response(await resp.text());

              },

            };

          `,

        },

        globalOutbound: ctx.exports.HttpGateway({

          props: { tenantId },

        }),

      };

    });


    return worker.getEntrypoint().fetch(request);

  },

};


```

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/dynamic-workers/","name":"Dynamic Workers"}},{"@type":"ListItem","position":3,"item":{"@id":"/dynamic-workers/usage/","name":"Usage"}},{"@type":"ListItem","position":4,"item":{"@id":"/dynamic-workers/usage/egress-control/","name":"Egress control"}}]}
```
