cloudflare / cloudflare/workers-sdk
🐛 BUG: `wrangler dev` Container's proxy-everything sidecar resets upstream HTTPS to non-handled hosts (despite default `interceptHttps: false`)
- Dominant language
- TypeScript
- Stars
- 4.5k
- Forks
- 1.5k
- Avg merge
- 3d 8h
- Merged PRs (30d)
- 186
Description
### What versions & operating system are you using?
```
System:
OS (host): Windows 11 10.0.26200
OS (runtime): Ubuntu 24.04 LTS via WSL 2.3.24.0
CPU: (28) x64 Intel(R) Core(TM) i7-14700K
Memory: 28.82 GB / 63.69 GB
Binaries (Windows side):
Node: 24.14.1
npm: 11.12.1
pnpm: 10.33.0
npmPackages:
@cloudflare/containers: ^0.2.0 => 0.2.4
@cloudflare/vitest-pool-workers: ^0.14.6 => 0.14.6
@cloudflare/workers-types: ^4.20241022.0 => 4.20260413.1
wrangler: ^4.83.0 => 4.83.0
```
**Important caveat about the WSL2 kernel:** the default WSL2 kernel does not include the `xt_socket` netfilter module that `proxy-everything`'s TPROXY rules require, so `wrangler dev` Containers cannot start at all on a stock WSL2 install (they fail at proxy-everything sidecar startup). I rebuilt the WSL2 kernel from [microsoft/WSL2-Linux-Kernel](https://github.com/microsoft/WSL2-Linux-Kernel) `linux-msft-wsl-5.15.y` with `CONFIG_NETFILTER_XT_MATCH_SOCKET=y` + the full TPROXY family enabled. Reports as `5.15.167.4-microsoft-standard-WSL2+`. After that, Containers DO start and bind cleanly. **The bug described below is what I observed AFTER getting past that prerequisite.** Mentioning this upfront so the bug isn't bounced as "unusual environment" — the counter-test below isolates blame from the kernel.
Also running `dockerd` directly inside the Ubuntu distro (not Docker Desktop's WSL Integration) because the latter mounts the docker socket but does not expose Docker's bridge network (`172.17.0.0/16`) inside the WSL distro's network namespace, so `wrangler dev`'s workerd fails to bind to `172.17.0.1` (Cannot assign requested address). In-distro `dockerd` puts the bridge in the right netns.
### Please provide a link to a minimal reproduction
I do not have a public repro repo, but the full reproduction is self-contained in this issue body. A minimum-viable Container Dockerfile + a one-line Worker invocation + the `wrangler.toml` snippet are in the "Describe the Bug" section below. The bug is deterministic from the snippet — happy to put it in a public gist on request if that helps the maintainer's repro.
### Describe the Bug
**Command:**
```bash
wrangler dev # inside WSL Ubuntu, with in-distro dockerd
```
**Minimal `wrangler.toml`:**
```toml
[[containers]]
name = "tse-prep"
class_name = "TseContainer"
image = "./containers/tse-prep/Dockerfile"
max_instances = 1
instance_type = "basic"
[[durable_objects.bindings]]
name = "TSE_PREP"
class_name = "TseContainer"
```
**Minimal Container Dockerfile** (`./containers/tse-prep/Dockerfile`):
```dockerfile
FROM alpine:3.20
RUN apk add --no-cache wget ca-certificates
CMD ["sh", "-c", "wget --spider -S https://cdn.tse.jus.br/cms/bem_candidato/bem_candidato_2024.zip 2>&1; sleep 3600"]
```
**Worker entrypoint** registers an `outboundByHost` handler ONLY for an alias like `ndjson.r2` (NOT for `cdn.tse.jus.br`), then calls `env.MY_CONTAINER.fetch('http://localhost/')` to wake the Container. The Container then attempts the wget shown in CMD.
**What happens:** `fetch()` (or `wget` inside the Container) to any HTTPS host that is NOT registered in `outboundByHost` resets the TLS handshake immediately:
```
Connecting to cdn.tse.jus.br (104.18.x.x:443)
wget: TLS error from peer (alert code 80): internal error
ssl_client: SSL_connect: Connection reset by peer
wget: error getting response: Connection reset by peer
```
**What should happen** per the [Container outbound-traffic docs](https://developers.cloudflare.com/containers/platform-details/outbound-traffic/), which state verbatim:
> **By default, HTTPS traffic is not intercepted by outbound handlers. To opt in you must set the `interceptHttps` attribute.**
The Container class does NOT set `interceptHttps`. So HTTPS to non-handled hosts should pass through to the public internet unchanged. Instead it gets reset.
#### Counter-test (isolates blame to the `proxy-everything` sidecar)
The SAME Docker image (the one `wrangler dev` built and tagged `cloudflare-dev/tsecontainer:`), run with `docker run` standalone on a custom Docker bridge with NO Cloudflare runtime and NO `proxy-everything` sidecar attached, fetches the same URL cleanly:
```
OK 200 content-length=45742138
```
This rules out:
- The Container image itself (same image works)
- The Container's `fetch()` / `wget` code (same code works)
- The custom WSL2 kernel (same kernel runs the standalone test successfully)
- Docker / Ubuntu / network config (same daemon serves both tests)
The only difference is the presence of the `proxy-everything` sidecar `wrangler dev` injects in front of the Container.
#### Suspected component
`proxy-everything` uses TPROXY iptables rules to intercept outbound from the Container. The TPROXY rules appear to apply blanket to both HTTP and HTTPS at L4, but the userland handler that's supposed to bridge non-handled HTTPS through to the public internet either isn't wired up or terminates the TLS handshake mid-flight. The "Connection reset by peer" during `SSL_connect` is consistent with TPROXY redirecting the SYN+handshake to a userland socket that immediately closes.
(I checked [github.com/cloudflare/proxy-everything](https://github.com/cloudflare/proxy-everything) — repo exists with 16 commits, no published releases, 0 open issues. README describes it as "a TPROXY based docker container sidecar." Filing here rather than there because (a) workers-sdk is where wrangler users file issues, and (b) the repo's relationship to wrangler dev isn't documented publicly — your team can route if this should live in `proxy-everything`.)
#### Notes for triagers
- **Affects only `wrangler dev` local runtime, NOT production Cloudflare Containers.** The same Worker + Container code, deployed to Cloudflare, would (per docs) fetch the same URL successfully. This is a local-dev parity bug.
- **Does not require the custom WSL2 kernel to reproduce** — the kernel is just what made `wrangler dev` start the Container at all on a Windows+WSL host. Once started, the `proxy-everything` HTTPS reset would presumably occur on any Linux host running `wrangler dev` Containers (including macOS via Docker Desktop / colima — would appreciate confirmation from a maintainer's environment).
- **Workaround (for my use case):** I bypass `wrangler dev` entirely for live verification — `docker run` the same image standalone on a Docker bridge with a `fake-r2` sidecar (network-alias `ndjson.r2`) and `curl` directly to the Container's HTTP endpoint. This works but loses the Worker ⇆ Container dispatch path; only useful for verifying Container-internal code.
### Please provide any relevant error logs
Container-side (full sequence):
```
Connecting to cdn.tse.jus.br (104.18.x.x:443)
wget: TLS error from peer (alert code 80): internal error
ssl_client: SSL_connect: Connection reset by peer
wget: error getting response: Connection reset by peer
```
Worker-side: no error. The Worker's `outboundByHost` handler for `ndjson.r2` receives traffic correctly; only non-handled HTTPS hosts are broken.
`proxy-everything` sidecar's stdout/stderr: not captured (no easy way to attach to it through `wrangler dev`). Happy to capture if you can point me at the right command.
Contributor guide
Assessment
This issue has not been assessed yet.