Infinite loop when HTTP_PROXY set

Alright, it took me a while, but I have an explanation!

First, you have to know that Caddy passes the original Host header to upstream instead of using the upstream’s own address in the upstream request. This is critical to understand what’s happening here.

Second, the behavior of the Go runtime when it comes to handling the HTTP_PROXY address includes this caveat:

As a special case, if req.URL.Host is “localhost” (with or without a port number), then a nil URL and nil error will be returned.

Meaning, it checks if the dialed URL (the upstream, not the proxy) is localhost, 127.x.x.x, or the IPv6 representation of loopback; and if the upstream URL is any of those, the Go runtime will not pass it through the URL specified in HTTP_PROXY. This is why these workarounds work

But why is the issue happening in the first place?
Well, with this config…

… this is the request journey:
1- Caddy receives a request on localhost:80
2- The dialed upstream address is 0.0.0.0:8080, so the Go runtime uses the URL specified in the HTTP_PROXY.
3- Caddy passes the original Host header
4- The proxy server receives the request with the value of the Host being localhost:80, per Caddy’s default behavior
5- The proxy server calls the service specified in the Host header, which is localhost:80, which is Caddy
6- Go back to 1.

My point to Francis still stands. The address 0.0.0.0 works by accident. It’s best to be explicit, especially now that we’ve seen the complexity it entails, but that could be me :slight_smile:

3 Likes