Handle outbound traffic
Last updated Oct 12, 2026 Outbound handlers let you intercept and modify HTTP traffic from a container with trusted code.
Use them to:
- Allow or deny specific origin destinations
- Safely inject authorization headers or tokens
- Transparently reroute traffic
- Add custom policy on outbound traffic (such as denying specific HTTP requests)
- Connect to Workers bindings like KV, R2, and Durable Objects
Block outbound traffic
Use enableInternet = false to block public internet access by default:
import { Container } from "@cloudflare/containers";
export class MyContainer extends Container {
enableInternet = false;
}
When enableInternet is false, only traffic that allowedHosts or an outbound handler allows can leave the container. Only ports 80 and 443 are available. Your outbound configuration decides which DNS lookups resolve. For more information, refer to Non-HTTP traffic.
Note
enableInternet takes effect when the container starts. Changes to outbound handlers and related outbound policies can affect a live-running container without restarting it.
Block or allow traffic by host
You can filter outbound traffic with the allowedHosts and deniedHosts properties on the Container class.
Note
Export ContainerProxy from your Worker entrypoint for outbound interception to work.
When allowedHosts is set, it becomes a deny-by-default allowlist. Any host or IP not in the list is denied, and only matching destinations can reach outbound or outboundByHost handlers.
allowedHosts and deniedHosts also support simple glob patterns where * matches any sequence of characters.
By default, a Container will allow internet access, and you can set deniedHosts to disallow specific hosts or IPs:
import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };
export class MyContainer extends Container {
// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
interceptHttps = true;
deniedHosts = ["some-nefarious-website.com", "141.101.64.0/18"];
}
Caution
deniedHosts applies to HTTP on port 80. With interceptHttps = true, it also applies to HTTPS on port 443. While enableInternet is true, connections to other ports bypass deniedHosts. To block a destination on every port, set enableInternet = false and allow only what the container needs.
You can also disable internet access by default, but allow specific hosts and IPs:
import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };
export class MyContainer extends Container {
// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
interceptHttps = true;
// default internet access to off unless overridden by 'allowedHosts' or outbound proxy
enableInternet = false;
// overrides enableInternet = false
allowedHosts = ["allowed.com"];
}
Define outbound handlers
Outbound handlers are programmable egress proxies that run on the same machine as the container. They have access to all Workers bindings.
Use outbound to intercept HTTP requests on port 80 and HTTPS requests on port 443:
import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };
export class MyContainer extends Container {
interceptHttps = true;
}
MyContainer.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);
};
Note
HTTP requests to the outbound handler remain secure because they run on the same machine as the container. You can upgrade requests to HTTPS from the Worker itself to prevent plain-text traffic from reaching the internet.
Use outboundByHost to map specific domain names or IP addresses to proxy functions:
import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };
export class MyContainer extends Container {
interceptHttps = true;
}
MyContainer.outboundByHost = {
"my.worker": async (request, env, ctx) => {
// Run arbitrary Workers logic from this hostname
return await someWorkersFunction(request.body);
},
};
Calls to http://my.worker from the container invoke the handler, which runs inside the Workers runtime, outside the container sandbox.
deniedHosts and allowedHosts are evaluated before any outbound handler. If you use allowedHosts, include the hostname there for either outbound or outboundByHost to run. outboundByHost handlers take precedence over catch-all outbound handlers.
Securely inject credentials
Because outbound handlers run in the Workers runtime — outside the container sandbox — they can hold secrets that the container itself never sees. The container makes a plain HTTP request, and the handler attaches the credential before forwarding it to the upstream service.
export class MyContainer extends Container {
// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
interceptHttps = true;
}
MyContainer.outboundByHost = {
"github.com": (request, env, ctx) => {
const requestWithAuth = new Request(request);
requestWithAuth.headers.set("x-auth-token", env.SECRET);
return fetch(requestWithAuth);
},
};
This is especially useful for agentic workloads where you cannot fully trust the code running inside the container. With this pattern:
- No token is exposed to the container. The secret lives in the Worker's environment and is never passed into the sandbox.
- No token rotation inside the container. Rotate the secret in your Worker's environment and every request picks it up immediately.
- Per-host and per-instance rules. Combine
outboundByHostwithctx.containerIdto scope credentials or permissions to a specific container instance.
Here, ctx.containerId looks up a per-instance key from KV:
export class MyContainer extends Container {
// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
interceptHttps = true;
}
MyContainer.outboundByHost = {
"my-internal-vcs.dev": async (request, env, ctx) => {
const authKey = await env.KEYS.get(ctx.containerId);
const requestWithAuth = new Request(request);
requestWithAuth.headers.set("x-auth-token", authKey);
return fetch(requestWithAuth);
},
};
HTTPS traffic
By default, HTTPS traffic is not intercepted by outbound handlers. To opt in you must set the interceptHttps attribute.
export class MyContainer extends Container {
// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
interceptHttps = true;
}
MyContainer.outbound = (req, env, ctx) => {
// All HTTP(S) requests will trigger this hook.
return fetch(req);
};
This is useful for Sandbox-like services that redirect untrusted traffic from a container instance to Workers for filtering and modification.
When HTTPS interception is active, an ephemeral CA file will be created at /etc/cloudflare/certs/cloudflare-containers-ca.crt once your container starts. The CA is only injected when you both set interceptHttps = true and define an outbound or outboundByHost handler.
Trust the CA certificate
For HTTPS interception to work, you must trust the CA file. The CA is ephemeral and only exists at runtime, so do not try to bake it into your image during docker build. Instead, copy it into your distro's trust store and refresh the trust store from the container entrypoint before your application starts.
If your base image does not already include the trust-store tooling, install the distro's ca-certificates package in your image first.
import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };
export class MyContainer extends Container {
interceptHttps = true;
entrypoint = [
"sh",
"-lc",
[
"cp /etc/cloudflare/certs/cloudflare-containers-ca.crt /usr/local/share/ca-certificates/cloudflare-containers-ca.crt",
"update-ca-certificates",
"exec node server.js",
].join(" && "),
];
}
import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };
export class MyContainer extends Container {
interceptHttps = true;
entrypoint = [
"sh",
"-lc",
[
"cp /etc/cloudflare/certs/cloudflare-containers-ca.crt /usr/local/share/ca-certificates/cloudflare-containers-ca.crt",
"update-ca-certificates",
"exec node server.js",
].join(" && "),
];
}
import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };
export class MyContainer extends Container {
interceptHttps = true;
entrypoint = [
"sh",
"-lc",
[
"cp /etc/cloudflare/certs/cloudflare-containers-ca.crt /etc/pki/ca-trust/source/anchors/cloudflare-containers-ca.crt",
"update-ca-trust",
"exec node server.js",
].join(" && "),
];
}
import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };
export class MyContainer extends Container {
interceptHttps = true;
entrypoint = [
"sh",
"-lc",
[
"cp /etc/cloudflare/certs/cloudflare-containers-ca.crt /etc/ca-certificates/trust-source/anchors/cloudflare-containers-ca.crt",
"trust extract-compat",
"exec node server.js",
].join(" && "),
];
}
Replace node server.js with the command that starts your application.
Most runtimes will then trust the CA through the system root store automatically. If your runtime uses its own CA bundle, point it at /etc/cloudflare/certs/cloudflare-containers-ca.crt directly, for example with NODE_EXTRA_CA_CERTS or REQUESTS_CA_BUNDLE.
Note
HTTP communication to the outbound handler is encrypted by the networking stack. For traffic that stays within the Cloudflare Developer Platform, plain HTTP is secure.
Non-HTTP traffic
Outbound handlers only intercept HTTP and HTTPS traffic. Traffic on ports other than 80 and 443 is never routed through outbound or outboundByHost.
If you set enableInternet = false, that traffic is denied. DNS lookups never reach a public resolver, so DNS cannot carry data out of the container. The container network answers lookups only for hostnames that your outbound configuration covers. Each answer is a placeholder address that routes the request to your Worker. The properties you set decide which hostnames resolve:
- With
allowedHosts,deniedHosts, oroutboundset, every hostname resolves, and your Worker allows or blocks each request. - With only
outboundByHostset, only its hostnames resolve.
Other lookups time out. For the exact rules, refer to interceptOutboundHttp.
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().
You can also manage runtime policy with setOutboundByHosts(), setAllowedHosts(), setDeniedHosts(), allowHost(), denyHost(), removeAllowedHost(), and removeDeniedHost().
This lets a trusted Worker hold credentials without exposing them to an untrusted container:
export class MyContainer extends Container {
// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
interceptHttps = true;
}
MyContainer.outboundHandlers = {
authenticatedGithub: async (request, env, ctx) => {
const githubToken = env.GITHUB_TOKEN;
return authenticateGitHttpsRequest(request, githubToken, ctx.containerId);
},
};
Apply handlers to hosts programmatically from your Worker:
async setUpContainer(req, env) {
const container = await env.MY_CONTAINER.getByName("my-instance");
// Give the container access to github.com on a specific host during setup
await container.setOutboundByHost("github.com", "authenticatedGithub");
// do something with github.com on your container...
}
async removeAccessToGithub(req, env) {
const container = await env.MY_CONTAINER.getByName("my-instance");
// Remove access to Github
await container.removeOutboundByHost("github.com");
}
Handler precedence
Requests are evaluated in this order:
deniedHostsis checked first. Matching hosts or IPs are denied immediately.allowedHostsis checked next. When it is set, any host or IP not in the list is denied. Matching hosts continue to outbound handlers, or egress to the public internet if no handler is set.- Instance-level rules set with
setOutboundByHost()are checked before class-leveloutboundByHostrules. - Per-host handlers always take precedence over catch-all handlers, so
outboundByHostruns beforeoutbound. - Instance-level handlers set with
setOutboundHandler()are checked before the class-leveloutboundhandler. - If no handler matches, the request can still egress to the public internet when it matched
allowedHostsorenableInternet = true. Otherwise, it is denied.
Direct API
To configure outbound interception directly on ctx.container, use interceptOutboundHttp for a specific hostname glob, IP, or CIDR range, or interceptAllOutboundHttp for all HTTP on port 80. Both accept a WorkerEntrypoint.
import { WorkerEntrypoint } from "cloudflare:workers";
export class MyOutboundWorker extends WorkerEntrypoint {
fetch(request) {
// Inspect, modify, or deny the request before passing it on
return fetch(request);
}
}
// Inside your Container DurableObject
this.ctx.container.start({ enableInternet: false });
const worker = this.ctx.exports.MyOutboundWorker({ props: {} });
await this.ctx.container.interceptAllOutboundHttp(worker);
You can call these methods before or after starting the container, and even while connections are open. In-flight TCP connections pick up the new handler automatically — no connections are dropped.
// Intercept a specific CIDR range
await this.ctx.container.interceptOutboundHttp("203.0.113.0/24", worker);
// Intercept by hostname
this.ctx.container.interceptOutboundHttp("foo.com", worker);
// Update the handler while the container is running
const updated = this.ctx.exports.MyOutboundWorker({
props: { phase: "post-install" },
});
await this.ctx.container.interceptOutboundHttp("203.0.113.0/24", updated);
For HTTPS, interceptOutboundHttps works the same way as interceptOutboundHttp.
// Intercept a specific hostname
this.ctx.container.interceptOutboundHttps("foo.com", worker);
// Intercept HTTPS to every hostname
this.ctx.container.interceptOutboundHttps("*", worker);
The Container class calls these methods automatically when you use the functions shown above. You can also call them directly for cases the class does not cover.
Local development
wrangler dev supports outbound interception. A sidecar process is spawned inside the container's network namespace. It applies TPROXY rules to route matching traffic to the local Workerd instance, mirroring production behavior.
Related resources
- Connect to Workers bindings — Access KV, R2, Durable Objects, and other bindings from a container
- Control outbound traffic (Sandboxes) — Sandbox SDK API for outbound handlers
- Environment variables and secrets — Configure secrets and environment variables
- Durable Object Container API — Full
ctx.containerAPI reference
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/containers/configuration/outbound-traffic/#page","headline":"Handle outbound traffic","description":"Intercept and handle outbound HTTP from containers using Workers.","url":"https://developers.cloudflare.com/containers/configuration/outbound-traffic/","inLanguage":"en","image":"https://developers.cloudflare.com/containers/configuration/outbound-traffic/og.png?v=2e06fa362fbb8548","dateModified":"2026-10-12","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
description: How container instances update after a deploy, including step percentages, grace periods, and rollout modes. title: Rollouts image: https://developers.cloudflare.com/containers/configuration/rollouts/og.png?v=3d0c4ea5b7db40b1
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/containers/llms.txt
Use this file to discover all available pages before exploring further.
Docs