Skip to content

Handle outbound traffic

Outbound Workers are Workers that handle HTTP requests made by your sandbox. They act as programmable egress proxies, running on the same machine as the sandbox with access to all Workers bindings.

Use outbound Workers to route requests to Workers functions and their bindings (KV, R2, Durable Objects, etc.)

Defining outbound handlers

Use outbound to intercept outbound HTTP traffic regardless of destination:

JavaScript
import { Sandbox } from "@cloudflare/sandbox";
export class MySandbox extends Sandbox {}
MySandbox.outbound = async (request, env, ctx) => {
if (request.method !== "GET") {
console.log(`Blocked ${request.method} to ${request.url}`);
return new Response("Method Not Allowed", { status: 405 });
}
return fetch(request);
};

Use outboundByHost to map specific domain names or IP addresses to handler functions:

JavaScript
import { Sandbox } from "@cloudflare/sandbox";
export class MySandbox extends Sandbox {}
MySandbox.outboundByHost = {
"my.worker": async (request, env, ctx) => {
// Run arbitrary Workers logic from this hostname
return await someWorkersFunction(request.body);
},
};

The sandbox calls http://my.worker and the handler runs entirely inside the Workers runtime, outside of the sandbox.

If you define both, outboundByHost handlers take precedence over the catch-all outbound handler.

Use Workers bindings in handlers

Outbound handlers have access to your Worker's bindings. Route sandbox traffic to internal platform resources without changing application code.

JavaScript
export class MySandbox extends Sandbox {}
MySandbox.outboundByHost = {
"my.kv": async (request, env, ctx) => {
const url = new URL(request.url);
const key = url.pathname.slice(1);
const value = await env.KV.get(key);
return new Response(value ?? "", { status: value ? 200 : 404 });
},
"my.r2": async (request, env, ctx) => {
const url = new URL(request.url);
// Scope access to this sandbox's ID
const path = `${ctx.containerId}${url.pathname}`;
const object = await env.R2.get(path);
return new Response(object?.body ?? null, { status: object ? 200 : 404 });
},
};

The sandbox calls http://my.kv/some-key and the outbound handler resolves it using the KV binding.

Access Durable Object state

The ctx argument exposes containerId, which lets you interact with the sandbox's own Durable Object from an outbound handler.

JavaScript
export class MySandbox extends Sandbox {}
MySandbox.outboundByHost = {
"get-state.do": async (request, env, ctx) => {
const id = env.MY_SANDBOX.idFromString(ctx.containerId);
const stub = env.MY_SANDBOX.get(id);
// Assumes getStateForKey is defined on your DO
return stub.getStateForKey(request.body);
},
};

Change policies at runtime

Use outboundHandlers to define named handlers, then assign them to specific hosts at runtime using setOutboundByHost(). You can also apply a handler globally with setOutboundHandler().

JavaScript
import { Sandbox } from "@cloudflare/sandbox";
export class MySandbox extends Sandbox {}
MySandbox.outboundHandlers = {
kvAccess: async (request, env, ctx) => {
const key = new URL(request.url).pathname.slice(1);
const value = await env.KV.get(key);
return new Response(value ?? "", { status: value ? 200 : 404 });
},
};

Apply handlers to hosts programmatically from your Worker:

JavaScript
import { getSandbox } from "@cloudflare/sandbox";
export default {
async fetch(request, env) {
const sandbox = getSandbox(env.Sandbox, "agent-session");
// Give the sandbox access to KV on a specific host during setup
await sandbox.setOutboundByHost("my.kv", "kvAccess");
await sandbox.exec("node setup.js");
// Remove access once setup is complete
await sandbox.removeOutboundByHost("my.kv");
},
};

Local development

wrangler dev supports outbound interception. A sidecar process is spawned inside the sandbox's network namespace. It applies TPROXY rules to route matching traffic to the local Workerd instance, mirroring production behavior.