# How to Configure Global Outbound and Network Services in Workerd

> Configure global outbound and network services in Workerd by setting the globalOutbound field and defining network access rules. Secure your worker's network connections effectively.

- Repository: [Cloudflare/workerd](https://github.com/cloudflare/workerd)
- Tags: how-to-guide
- Published: 2026-03-18

---

**You configure global outbound and network services in workerd by setting the `globalOutbound` field in your worker's Cap'n Proto configuration to a service name, `null`, or another worker binding, while defining network access rules in the service's `network` block with `allow` and `deny` lists.**

Workerd, the open-source JavaScript/Wasm runtime from Cloudflare, routes all outbound network traffic—such as `fetch()` requests—through a configurable **global outbound service**. This architecture lets you control internet access, implement proxy layers, or completely sandbox workers by editing the Cap'n Proto schemas in `src/workerd/server/workerd.capnp`.

## Understanding the Global Outbound Selector

The `globalOutbound` field in a worker definition determines where outbound traffic resolves. According to `src/workerd/server/workerd.capnp` (lines 998–1002), this field accepts a **ServiceDesignator** that can reference three distinct targets:

- **Default "internet" service** — If you omit `globalOutbound` or leave it unset, the runtime automatically uses the built-in service named `"internet"`. This service must be defined elsewhere in your configuration and typically provides unrestricted public internet access.

- **Custom service binding** — Setting `globalOutbound = "my-proxy"` forces every `fetch()` in that worker to resolve through the service named `my-proxy`. This can point to an external proxy, a local socket, or another internal endpoint.

- **Null outbound** — Specifying `globalOutbound = null` completely disables outbound networking. Any attempt to call `fetch()` throws a `DOMDataCloneError`, as implemented in `src/workerd/server/server.c++` around line 4074.

- **Worker-to-Worker proxy** — The selector can reference another worker binding, allowing a worker to act as a middleware proxy for all outbound requests from its parent.

## Network Access Control in Service Definitions

Every service in workerd can include a `network` block that defines host-level allow and deny lists. In `src/workerd/server/workerd.capnp` (around lines 800–820), the `Service` struct contains these fields:

- **`allow`** — An array of hostnames or IP addresses permitted for outbound connections. An empty list blocks all traffic.
- **`deny`** — An array of hosts explicitly blocked, even if they appear in the `allow` list.

If both fields are omitted, the service grants unrestricted outbound access. The test case `KJ_TEST("Server: network outbound with allow/deny")` in `src/workerd/server/server-test.c++` (lines 3289–3297) validates that requests to allowed hosts succeed while denied hosts return errors.

## Practical Configuration Examples

### Default Internet Outbound

The minimal configuration relies on the implicit `"internet"` service. Define an unrestricted network service and a worker without an explicit `globalOutbound` field:

```capnp
services = [
  (name = "internet", network = (allow = []))
]
sockets = [
  (name = "main", address = "0.0.0.0:8080", service = "my-worker")
]
workers = [
  (name = "my-worker", entrypoint = "worker.js")
]

```

Because `globalOutbound` is omitted, the worker inherits the default `"internet"` service and can reach any public host.

### Custom Proxy Service

Route all outbound traffic through a specific proxy endpoint with restricted egress:

```capnp
services = [
  (name = "internet", network = (allow = [])),
  (name = "my-proxy",
    network = (allow = ["api.example.com"]),
    external = (address = "10.0.0.5", http = (style = proxy))
  )
]

workers = [
  (name = "app",
    entrypoint = "app.js",
    globalOutbound = "my-proxy"
  )
]

```

Here, the **app** worker sends every `fetch()` to the proxy at `10.0.0.5`, and the proxy's `allow` list restricts egress to `api.example.com` only.

### Worker-to-Worker Proxy

Use another worker as the global outbound to implement custom logic such as caching or authentication:

```capnp
workers = [
  (name = "outbound-proxy",
    entrypoint = "proxy.js"
    # Inherits default "internet" for its own outbound

  ),
  (name = "frontend",
    entrypoint = "frontend.js",
    globalOutbound = (name = "outbound-proxy", worker = "outbound-proxy")
  )
]

```

The **frontend** worker delegates all `fetch()` calls to the **outbound-proxy** worker, which can inspect, modify, or log requests before forwarding them.

### Disabling Outbound Traffic

Create a fully sandboxed worker that cannot make network requests:

```capnp
workers = [
  (name = "sandboxed",
    entrypoint = "sandbox.js",
    globalOutbound = null
  )
]

```

Any `fetch()` invocation inside **sandboxed** immediately throws a `DOMDataCloneError`.

### Fine-Grained Allow and Deny Lists

Implement precise host-level restrictions using both lists:

```capnp
services = [
  (name = "restricted",
    network = (
      allow = ["private", "api.internal"],
      deny  = ["bad.example.com"]
    )
  )
]

workers = [
  (name = "service",
    entrypoint = "service.js",
    globalOutbound = "restricted"
  )
]

```

This configuration allows connections to `private` and `api.internal`, but explicitly blocks `bad.example.com` even if it resolves within the allowed ranges.

## Runtime Implementation and Inheritance

The runtime wiring resides in `src/workerd/server/server.c++` (lines 3998–4400), where the `FutureSubrequestChannel globalOutbound` handles request dispatch. When a worker loads another worker via the `Worker` API, inheritance rules defined in `src/workerd/api/worker-loader.c++` (lines 115–165) apply: child workers inherit the parent's `globalOutbound` selector unless their own configuration explicitly overrides it or sets it to `null`.

## Using Outbound Services from JavaScript

Regardless of the backend configuration, JavaScript code uses the standard `fetch()` API. The runtime automatically routes requests through the configured global outbound channel:

```javascript
export default {
  async fetch(request, env, ctx) {
    // Automatically routed through the worker's globalOutbound service
    const resp = await fetch("https://api.example.com/data");
    return resp;
  }
};

```

If the worker's `globalOutbound` is set to `null`, this call throws before the network stack is reached.

## Summary

- **Default behavior** — Omitting `globalOutbound` routes traffic through the built-in `"internet"` service.
- **Service designators** — Set `globalOutbound` to a string (service name), `null` (disable), or a worker binding (proxy).
- **Network rules** — Define `allow` and `deny` lists in the service's `network` block to whitelist or blacklist specific hosts.
- **Inheritance** — Child workers inherit the parent's global outbound unless explicitly overridden, as handled in `worker-loader.c++`.
- **Sandboxing** — Set `globalOutbound = null` to completely disable outbound requests and throw `DOMDataCloneError` on `fetch()`.

## Frequently Asked Questions

### What happens if I omit the globalOutbound field in my worker configuration?

The runtime defaults to the service named `"internet"`. According to `src/workerd/server/workerd.capnp`, the `globalOutbound @6` field has a default value of `"internet"`, meaning the worker automatically uses that service for all outbound connections unless you specify otherwise.

### How do allow and deny lists interact in network service definitions?

The `deny` list takes precedence over the `allow` list. If a host appears in both lists, the connection is blocked. If only `allow` is specified, only those hosts are reachable; if `allow` is empty, no outbound traffic is permitted. When both fields are absent, the service allows unrestricted access.

### Can I use another Worker to process all outbound fetch requests?

Yes. Set `globalOutbound` to a worker binding designator like `(name = "proxy", worker = "proxy-worker")`. This routes every `fetch()` through the specified worker, which can implement custom logic before forwarding the request to its own global outbound service.

### What error does workerd throw when globalOutbound is set to null?

When `globalOutbound = null`, any attempt to call `fetch()` throws a `DOMDataCloneError`. This behavior is explicitly implemented in `src/workerd/server/server.c++` to signal that the worker has no outbound channel configured.