If Cloudflare Tunnel terminates TLS, what is left for Caddy to do?
Felix Braun
October 7, 2026
Routing. Caddy still chooses which container gets the request, and it can still speak HTTP to that container on the machine. It does not need to be the public certificate anymore. Cloudflare already ended TLS at the edge. What is left, when several apps share one tunnel, is the Host header and the upstream. If the tunnel’s own ingress rules already send each hostname to the right port, Caddy can leave. If you point the tunnel at one Caddy and let Caddy fan out, Caddy is the fan-out, not the lock on the door.
The mistake is running both as if each must mint a public certificate. Caddy then sits in a renew loop for a name that never hits port 80 on your router, because the tunnel is the only way in. The site works anyway, via the tunnel, and the logs fill with ACME failures. Turn the public certificate off for those names. Give Caddy the plain HTTP site, or an origin certificate Cloudflare signed, and let it proxy.
What the tunnel already finished
Cloudflare Tunnel terminates the visitor’s HTTPS. The browser trusts Cloudflare. cloudflared receives the request and opens a connection to an origin you configured, often http://localhost:80 or a docker address. That hop is not the public internet. On the same machine it is a local socket. TLS there is optional. People enable it because Caddy’s whole reputation is automatic HTTPS, and then they are surprised that automatic HTTPS has nothing to prove against. There is no inbound port 80. There is no Let’s Encrypt HTTP challenge. The certificate the world sees is Cloudflare’s.
So Caddy’s famous job is done by someone else. The jobs that remain are the ones you were already using Caddy for when it sat behind nothing: match a hostname, reverse-proxy to a port, pass websockets, set a header, maybe require a login in front of one app. Those do not need a public cert. A site block that says reverse_proxy localhost:8096 is the whole file for Jellyfin. Repeat it for the next app. That is Caddy earning its keep beside a tunnel.

cloudflared can do that fan-out itself. Ingress rules map hostname to URL. One rule for Jellyfin, one for the documents app, one for nothing else. In that design Caddy is a second router in a row, and the second router is how hostnames get lost. Pick one place that knows the map. If you already live in a Caddyfile, point the tunnel at Caddy and delete the duplicate ingress. If you have three services and no other reason for a proxy, put the three rules in the tunnel and do not run Caddy. Two maps will drift. The one that is wrong is the one you debug at midnight.
When the hop to Caddy should still be encrypted
Encrypt the hop when cloudflared and Caddy are not on the same trust boundary. cloudflared on a VPS, Caddy at home, the origin address a public IP or a LAN you do not fully trust: use HTTPS to the origin, and use a Cloudflare origin certificate on Caddy so Full (strict) has a cert it will accept. That certificate is not Let’s Encrypt and it does not need port 80. You install the origin cert and key, you tell Caddy to use those files, and you stop asking Caddy to solve an ACME challenge for a name the tunnel owns.
On one docker host, HTTP to Caddy is enough. The packets do not leave the machine. Wrapping them in a second TLS layer means another cert to expire and another mismatch when the Host header and the certificate name disagree. Full (strict) from Cloudflare to an HTTP origin is the wrong mode for that. The tunnel connector is already the encrypted path from the edge to your process. The dashboard setting that matters is the one between cloudflared and the origin, and localhost HTTP is a legitimate origin. I would rather have a boring Caddyfile than a strict mode that fails because the origin cert expired on a hostname nobody visits except through the tunnel.
Websockets and uploads are the traffic that tells you the proxy is still doing work. A tunnel alone, pointed at the right port, passes them. Caddy in the middle passes them if you did not buffer the body into oblivion or time out a long request. Cloudflare’s own proxy has a timeout. Caddy has a timeout. A backup upload or a large photo import that dies at a round number of seconds is one of those two, not a missing certificate. Lengthen the timeout on the hop that is cutting it, or stop uploading through the public name and use Tailscale for the heavy thing. The certificate was never the reason a two-gigabyte request failed.
What you should take off Caddy
Take off automatic HTTPS for any hostname that only exists on the tunnel. The renewal cannot succeed the way it did when you had a port forward, and the failure looks like the site is unhealthy when the site is fine. Take off a second redirect to HTTPS if Cloudflare already redirected and Caddy redirects again. Loops are how a working tunnel becomes a browser error. Take off HSTS experiments until you are sure you will keep the name behind Cloudflare. HSTS on a name you later move back home without a cert is a browser you cannot easily fix.
Leave Caddy in charge of anything the tunnel ingress is bad at remembering. A header the app needs, a strip of a prefix, a matcher for a path that should hit a different upstream, an internal-only site that is not on Cloudflare at all. The Caddyfile becomes shorter, not empty, in a house that has both local names and one public tunnel. Local names can still use Caddy’s real automatic HTTPS, or plain HTTP on the tailnet, because those names are not the tunnel’s. Do not make one setting cover both. The tunnel names are HTTP behind cloudflared. The tailnet names are whatever you want on the LAN. Mixing them in one site block is how a local debug URL starts asking the internet for a certificate.
Who can delete Caddy
Delete it if the tunnel ingress already names every public app and you do not use Caddy for anything else. One less process, one less file that can disagree with the tunnel. Keep it if you like the Caddyfile, you have more than a couple of apps, or some of those apps are only on the tailnet and Caddy is already their front door. The tunnel then has a single origin, Caddy, and Caddy has the list. That is a clean split. TLS ended at Cloudflare. Caddy is a router. Routers do not need to pretend they are the edge.
Test it by requesting the public name and reading which upstream answered, then stop Caddy only if nothing you use goes through it. If the public name dies, the tunnel was aimed at Caddy and Caddy was the router you still needed. If the public name lives and only a local name dies, you still needed Caddy for the local name. Either result is a reason. A renewing certificate is not. The edge already has one.
One more check belongs in the file, not in a dashboard. From a phone that is not on your tailnet, open the public hostname and confirm the app loads. From a machine that is on the tailnet, open the local name and confirm that path does not depend on Cloudflare. If both work, Caddy is doing the local job and the tunnel is doing the public job, and neither is renewing a certificate for the other. Keep the Caddyfile in the same backup as the tunnel token. Losing one and not the other is how a restore brings the apps back with no names, or the names back with no routes. The certificate files, if you still have an origin cert, are part of that backup. The Let’s Encrypt directory is not, for these hostnames, because you are not using it. Delete the habit of copying it as if it were the site. When a new app arrives, add one site block and one tunnel origin, not a new certificate email. The question was what is left. The answer is the route. Write the route down, test it from a phone off the tailnet, and stop configuring TLS for a door Cloudflare already closed. The next certificate error in the log is a leftover, not a new outage, and it can be removed without taking the hostname down or asking anyone to accept a warning in a browser that cached the real edge certificate. Leave the edge certificate where it already is. Caddy’s job is the route behind it, and that job is done when the right app answers. If only the public name works, you already know the tunnel is the edge. Leave Caddy out of that certificate. The router work is enough.