Windows Guide Tags: Docker Desktop Clash host.docker.internal HTTP_PROXY

Docker Desktop on Windows:Proxy Containers via Clash and host.docker.internal

Clash on Windows happily proxies your browser, but docker run and Docker Compose workloads often still go direct. That is expected: container traffic does not inherit the Windows “system proxy” story the way a normal Win32 app does. This guide explains the container network stack on Docker Desktop, how to reach the host’s Clash listener with host.docker.internal, which HTTP_PROXY / HTTPS_PROXY / NO_PROXY patterns actually work for outbound fetches and image pulls, and how to keep private registries and LAN endpoints off the tunnel. It sits alongside our WSL2 + Clash guide—same host, different sandbox.

Approx. 25 min read
Clash Editorial

1. What You Are Actually Debugging

When people say “Docker ignores Clash,” they usually mean one of three different failures: the container cannot reach the proxy port on the host at all; the container reaches the proxy but name resolution or TLS breaks inside the tunnel; or only some traffic should use the proxy while registry pulls and internal APIs must stay local. Docker Desktop on Windows runs your Linux containers inside a managed VM with bridge networking, embedded DNS, and its own default routes. That stack does not automatically read whatever proxy string you configured for Microsoft Edge or for the Clash tray app’s “Set System Proxy” toggle.

Your mental model should be: Windows applications and Linux containers are different attachment points. Browsers and many native apps honor WinINET-style proxy settings or loopback listeners on 127.0.0.1. A container’s outbound connection originates from the Docker bridge, hits the Linux routing table inside the VM, and only reaches your host’s TCP stack if you aim at a host-routable address and a port that is actually listening and permitted through Windows Firewall. Nothing about that path is implied by “Clash works in Chrome.”

This article standardizes on forwarding application-layer HTTP(S) traffic through Clash’s HTTP or mixed listener using the usual proxy environment variables. If you need full IP-layer capture for arbitrary UDP or exotic protocols, that is a different design—often TUN mode on the host—and you should read the trade-offs in our TUN troubleshooting guide before you assume one knob fixes every containerized workload.

2. Why System Proxy Does Not Reach Containers

Microsoft and Docker can surface “proxy” settings in several places: per-user WinHTTP proxy, per-app settings, and optional Docker Desktop proxy panes for the engine itself. None of these reliably inject HTTP_PROXY into every container process. Even when the Docker daemon is configured to pull images through a proxy, your docker run application process still starts with a clean environment unless you pass variables explicitly or bake them into the image.

Another common confusion is loopback. On the Windows host, 127.0.0.1:7890 might be exactly where Clash listens. Inside a container, 127.0.0.1 refers to the container’s own network namespace, not the Windows host. That is the same class of bug we document for WSL2—and the fix is also the same in spirit: stop aiming at loopback from the guest; aim at the host using a stable hostname or IP. For containers on Docker Desktop, the portable hostname is host.docker.internal, which resolves to a reachability address for the host from within the Linux VM.

Tip: Before editing YAML or Compose files, run a one-off container that only tests TCP to host.docker.internal:PORT with curl or wget. If that fails, no amount of application config will help until the listener and firewall path are fixed.

3. host.docker.internal and the Host IP

host.docker.internal is a special DNS name provided by Docker Desktop so containers can refer to services running on the machine that hosts Docker—your Windows PC. It is the right default choice for “send this container’s HTTP traffic to Clash on the host” because it tracks Docker’s networking implementation better than hard-coding an IPv4 address that might change when you switch Wi-Fi, VPN, or Docker backend updates.

Construct your proxy URL from three pieces: scheme http:// (typical when pointing at an HTTP or mixed proxy port), host host.docker.internal, and port—the same port you configured in Clash for inbound connections, often a mixed port such as 7890 unless you split HTTP and SOCKS explicitly. The resulting baseline looks like http://host.docker.internal:7890. If your stack expects SOCKS5 instead, switch the scheme and port to match the SOCKS listener defined in your profile.

When host.docker.internal is not enough

In rare setups—custom Docker networks, rootless experiments, or enterprise security tools—you might need the literal host gateway IP from the container’s perspective. You can discover it with Docker’s networking introspection or by inspecting interface routes inside a throwaway container. Treat those addresses as environment-specific: document them for your team, but prefer host.docker.internal in shared docker-compose.yml files when compatibility allows.

4. Clash Listen Address, LAN, and Firewall

Even with the correct hostname, the connection will fail if Clash only binds to 127.0.0.1 on the Windows side in a way that rejects packets arriving from the Docker internal bridge. In practice, GUI clients expose this as Allow LAN or an equivalent toggle; YAML-oriented setups talk about allow-lan and binding the mixed or HTTP port on 0.0.0.0 rather than loopback-only. The goal is simple: the host must accept inbound TCP to your proxy port from the virtual subnet Docker uses to reach the host.

Pair listener scope with Windows Firewall. A rule that allows the Clash executable—or the specific TCP port—on private networks prevents mysterious timeouts that look like “bad nodes” when the proxy never receives the SYN. Our dedicated walkthrough for mixed-port exposure is the Windows 11 mixed port and firewall article; apply the same listener and firewall discipline before you blame subscription quality.

Note: Do not confuse the external-controller REST API port with the user traffic port. Opening the wrong listener widens attack surface without fixing your app’s outbound HTTP calls.

5. HTTP_PROXY, HTTPS_PROXY, and Lowercase Variants

Most containers that need outbound HTTP(S) respect the classic environment variables: HTTP_PROXY and HTTPS_PROXY for forward proxy URLs, sometimes mirrored in lowercase for tools that follow POSIX conventions more strictly. A typical runtime invocation sets both to the same upstream when you terminate TLS at the proxy using an HTTP CONNECT tunnel—exactly how many Clash HTTP listeners behave for HTTPS clients.

For a one-off test with docker run, pass environment variables with -e flags:

docker run --rm -e HTTP_PROXY=http://host.docker.internal:7890 -e HTTPS_PROXY=http://host.docker.internal:7890 curlimages/curl:latest https://example.com

Adjust the port to match your profile. If your application uses its own configuration file for proxy settings—Node, Java, Go each differ—still set the environment as a baseline; many libraries consult it first.

all_proxy and SOCKS

When you route through SOCKS5 instead of HTTP CONNECT, tools may expect ALL_PROXY or all_proxy with a value like socks5://host.docker.internal:7891. Keep schemes consistent with the listener you enabled in Clash. Mixed ports that speak both HTTP and SOCKS can simplify operations, but your environment strings must still describe the protocol the client actually uses.

6. NO_PROXY for Registry, LAN, and Docker DNS

Blindly exporting HTTP_PROXY everywhere is how teams accidentally break pushes to a private registry, slow down pulls against a mirror that should be direct, or send RFC1918 traffic through an exit node that was never meant to see internal subnets. NO_PROXY (and no_proxy) lists hostnames, domain suffixes, and sometimes CIDR ranges that must bypass the proxy.

Concrete patterns worth including for many organizations:

  • Local registry hostnames: registry.internal.corp, harbor.local, or IP literals your team uses for on-prem registries.
  • Public registry endpoints you want direct or split-routed: Some teams proxy Docker Hub pulls; others mirror them internally—pick one story and encode it in NO_PROXY or the opposite, depending on which path must bypass.
  • Link-local and metadata: Cloud provider metadata IPs and localhost-style services often should never traverse an HTTP proxy.
  • Docker embedded DNS: Service discovery names like db or api inside user-defined Compose networks resolve to container IPs. Those East-West calls rarely belong in an outbound web proxy; add the short names or the .internal patterns your stack uses.

Be explicit about comma-separated lists and avoid trailing wildcards unless your toolchain documents support for them. When in doubt, test with a minimal repro container that prints effective environment values and then curl both a public URL and an internal hostname.

7. Docker Compose and env_file

Long-lived development stacks should centralize proxy variables in a Compose environment block or an env_file that you can disable in CI. A common pattern is a checked-in .env.example with HTTP_PROXY= placeholders and a local untracked .env that developers fill with http://host.docker.internal:7890. That keeps secrets out of Git while making the mechanism obvious to new teammates.

Remember service-to-service traffic: only add proxy variables to services that initiate outbound Internet fetches. A database container usually should not need global HTTPS_PROXY, and forcing one can confuse health checks that call internal-only endpoints. For front-end Node services that bundle during docker compose build, you may also need build-time arguments—covered next—because image build steps do not always inherit runtime environment blocks.

For broader configuration concepts—ports, modes, and rule philosophy—see the Clash configuration overview on this site while you align host and container networking.

8. docker build and BuildKit

Building an image often downloads npm packages, Debian packages, or Git repositories. Those downloads happen during the build phase, which is not identical to docker run. Pass proxy settings as build arguments and map them into HTTP_PROXY inside the Dockerfile with ARG and ENV instructions, or use Docker BuildKit’s forwarding of common proxy variables when enabled in your environment.

Typical patterns include docker build --build-arg HTTP_PROXY=... alongside HTTPS_PROXY, and ensuring NO_PROXY is available during apt-get or apk steps so corporate mirrors remain reachable. Cache layers interact with proxy changes: when the proxy URL changes, expect cache misses—this is normal and preferable to silently baking stale network assumptions into immutable layers.

9. Docker Desktop Proxy Settings vs Env Vars

Docker Desktop exposes optional proxy configuration meant primarily for the Docker engine and image distribution paths. Those settings help when the daemon itself must reach Docker Hub through a corporate forward proxy. They do not replace per-container HTTP_PROXY for arbitrary processes you launch—think of engine-level proxy as “pulls and pushes,” and container env as “my app’s outbound HTTP clients.” You may need both in restrictive networks.

If you enable engine proxy and also use Clash locally, be careful about double-proxying or contradictory exclusions. Align NO_PROXY between Docker daemon configuration and container environments so internal registry hostnames stay consistent across build and run phases.

10. Verification Checklist

Symptom Likely cause First fix
Connection refused to 127.0.0.1:7890 from container Loopback refers to the container, not the host Use host.docker.internal and host port
Timeout to host.docker.internal:PORT Clash bind scope or firewall Allow LAN / 0.0.0.0 bind; add firewall rule
Public sites work; private registry fails Missing or wrong NO_PROXY Add registry hostname or IP to bypass list
Runtime OK; build-time fetch fails Proxy not passed to build --build-arg / Dockerfile ARG+ENV

End-to-end confidence comes from layering checks: host-side curl through Clash, then the same URL from inside a throwaway container with identical environment variables, then your real service with application logs enabled. When those layers diverge, you know exactly which configuration surface still disagrees with reality.

11. Summary

Docker Desktop on Windows does not magically inherit your Clash system proxy for every container. Treat containers as first-class network clients: aim them at the host with host.docker.internal, forward HTTP(S) through HTTP_PROXY and HTTPS_PROXY, carve exceptions with NO_PROXY for registries and intranet hosts, and align Clash’s listener bind mode with Windows Firewall so the Docker bridge can actually deliver packets. Add build-time forwarding when image construction—not just runtime—needs the same path.

Compared with opaque all-in-one VPN clients, an explicit proxy environment is boring—which is why it composes well with containers. You can print the effective variables, diff Compose files, and teach new hires without hand-waving about “the OS should just know.” That transparency is the same reason rule-based cores remain popular among operators who split traffic by domain and process.

When you install or update the Windows client, use this site’s download page for traceable installers, then apply the host and container alignment above on a build you trust.

Download Clash for free and experience the difference