Primary job: A containerized cloudflared connector is healthy, but the public hostname returns 502 because the connector cannot reach the Docker origin service.
Quick answer
A Cloudflare-generated Tunnel 502 means the connector reached Cloudflare but could not reach the configured origin. An origin application can also return its own HTTP 502, so confirm the response and connector logs. In Docker, first compare the networks attached to cloudflared and the origin. If you use a service name such as http://app:8080, both containers must share a user-defined network. localhost inside cloudflared refers to the connector container itself, not another container.
Do not disable TLS verification first. A wrong network, stopped service, wrong port, protocol mismatch, and certificate error require different fixes.
What the live disposable tunnel test proved
Tested on 2026-09-04 with Docker Server 29.7.2 and an account-authorized cloudflared connector.
A delegated non-production hostname routed to http://bb-cf502-origin:8080. The disposable origin was initially attached only to an isolated test network, while cloudflared was attached to the connector network.
The origin loopback control returned BigBears Cloudflare 502 test origin OK. Through the public hostname, the same setup returned HTTP 502 with server: cloudflare and a cf-ray identifier. The matching connector log excerpt said:
Unable to reach the origin service: ... dial tcp: lookup bb-cf502-origin on 127.0.0.11:53: no such host
After attaching only the disposable origin to the user-defined network shared with cloudflared, the same public URL returned HTTP 200 and the expected body. No TLS setting, public port, application code, or production origin was changed. The disposable container and isolated network were removed after capture; the test image digest and cleanup results are retained in the private evidence packet.
Diagnose in this order
1. Confirm the origin itself is healthy
Run the request from the origin’s own network namespace or a known-good peer. Check the process and container port, not only the host-published port.
docker ps --format 'table {{.Names}}t{{.Status}}t{{.Ports}}'
docker exec <origin> wget -qO- http://127.0.0.1:<container-port>
2. Compare network membership
docker inspect -f '{{json .NetworkSettings.Networks}}' <cloudflared>
docker inspect -f '{{json .NetworkSettings.Networks}}' <origin>
If the configured origin is http://app:8080, the two containers need at least one deliberately shared user-defined network. Use the container port (8080 here), not a host-published port unless the connector intentionally routes through the host.
3. Test the exact origin URL from the connector network
The official cloudflare/cloudflared image is intentionally minimal. Use a disposable probe attached to the same network rather than modifying the running connector:
docker run --rm --network <connector-network>
busybox@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662
wget -T 3 -qO- http://<service-name>:<container-port>
A DNS failure usually means the service name is not registered on that network. connection refused means the address resolved but nothing accepted the selected port. A malformed HTTP response often points to an HTTP/HTTPS mismatch. An x509 error belongs to certificate validation, not Docker service discovery.
4. Apply the smallest network fix
In Compose, attach only the connector and the intended origin to a shared network. Keep databases and unrelated services off that network.
services:
cloudflared:
image: cloudflare/cloudflared:2026.6.1
networks: [tunnel]
app:
image: your-owned-image:tested-version
networks: [tunnel, backend]
networks:
tunnel: {}
backend:
internal: true
The route then uses the stable service name and container port:
service: http://app:8080
Do not pin a dynamic container IP. Docker Compose service names remain stable when containers are recreated; container IPs do not.
Failure matrix
| Evidence | Likely layer | Next check |
|---|---|---|
| Origin loopback fails | Origin process/port | Process status and listener |
| Service name does not resolve from connector network | Docker network/DNS | Shared network membership and service name |
| Name resolves but connection is refused | Wrong port or stopped listener | Container port and listener |
| Malformed HTTP response | Protocol mismatch | http:// vs https:// |
x509 error |
Origin certificate validation | SNI, CA pool, certificate name |
| Tunnel 1033 | Connector is not healthy at Cloudflare | Tunnel status and edge connectivity |
Rollback
If the shared-network change does not fix the exact origin probe:
- restore the previous Compose file,
- recreate only the affected connector/origin services,
- verify their previous networks from the saved
docker inspectoutput, - do not weaken TLS or expose a host port as a workaround unless that architecture is explicitly intended.
Boundaries
- This guide does not authorize bypassing Cloudflare Access, WAF, authentication, or certificate checks.
noTLSVerifyis not a network fix and should not be the default response to a 502.- Never put credentials, tunnel tokens, origin certificates, private hostnames, or unredacted request headers in logs or screenshots.
- The fixture did not change any production container or its configuration. The disposable origin container and isolated Docker network were removed and verified absent after capture. The delegated test hostname is managed separately in Cloudflare and should be deleted after this experiment.
Source ledger
- Cloudflare, “Troubleshooting,” checked 2026-08-30: https://developers.cloudflare.com/tunnel/troubleshooting/
- Docker, “Networking overview,” checked 2026-08-30: https://docs.docker.com/engine/network/
- Docker, “Networking in Compose,” checked 2026-08-30: https://docs.docker.com/compose/how-tos/networking/
Evidence and repeatability
The sanitized artifacts retain the failing edge response metadata, matching connector error, fixed edge response, version manifest, topology change, and rollback instructions. The public test used a disposable origin and a delegated non-production hostname; credentials and tunnel tokens were not recorded in the evidence.
