Reverse proxy to multiple backends using request header as key

1. Caddy version (caddy version):

v2.4.6 h1:HGkGICFGvyrodcqOOclHKfvJC0qTU7vny/7FhYp9hNw=

2. How I run Caddy:

Installed via apt, and uses systemd.

a. System environment:

Ubuntu 20.04 Linux.

b. Command:

/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile

d. My complete Caddyfile or JSON config:

proxy1.hooli.xyz {
    log {
        output file /var/log/caddy/access.log
    }

    reverse_proxy https://google.com {
        lb_policy header backend-google
        header_up Host {upstream_hostport}
        header_up X-Forwarded-Host {host}
        header_down -server
    }

    reverse_proxy https://yahoo.com {
        lb_policy header backend-yahoo
        header_up Host {upstream_hostport}
        header_up X-Forwarded-Host {host}
        header_down -server
    }

    reverse_proxy https://espn.com {
        lb_policy header backend-espn
        header_up Host {upstream_hostport}
        header_up X-Forwarded-Host {host}
        header_down -server
    }

    respond 404
}

3. The problem I’m having:

I am trying to reverse proxy to 3 different backends switching on a request header. For example if the header is backend-espn proxy to https://espn.com. If the header is not passed, or does not match a header key I am looking to return a 404 error.

Right now, it just returns 404 for all requests even when passing a valid header.

curl -i https://proxy1.hooli.xyz -H "backend-yahoo: true"

HTTP/2 404
server: Caddy
content-length: 0
date: Wed, 16 Feb 2022 19:47:18 GMT

You want to use request matchers here, not lb_policy.

lb_policy is for load balancing, i.e. choosing between multiple backends configured on a single reverse_proxy handler.

That said, I think what you’re really looking for here is a forward-proxy, not a reverse-proxy. Proxying to google and such isn’t really what you should be doing with a reverse-proxy.

@francislavoie thanks for the reply. I just put google.com, yahoo.com, and espn.com as examples. The reverse proxy backends are gonna be private endpoints like 10.0.0.2:1455.

I updated my Caddyfile to the following, but still not working correctly.

proxy1.hooli.xyz {
    log {
        output file /var/log/caddy/access.log
    }

    @google {
        header Proxy-Backend google
    }

    @yahoo {
        header Proxy-Backend yahoo
    }

    @espn {
        header Proxy-Backend espn
    }

    reverse_proxy @google https://google.com {
        header_up Host {upstream_hostport}
        header_up X-Forwarded-Host {host}
        header_down -server
    }

    reverse_proxy @yahoo https://yahoo.com {
        header_up Host {upstream_hostport}
        header_up X-Forwarded-Host {host}
        header_down -server
    }

    reverse_proxy @espn https://espn.com {
        header_up Host {upstream_hostport}
        header_up X-Forwarded-Host {host}
        header_down -server
    }

    respond 404
}

This should be proxying to Yahoo.com.

curl -i https://proxy1.hooli.xyz -H "Proxy-Backend: yahoo"
HTTP/2 404
server: Caddy
content-length: 0
date: Thu, 17 Feb 2022 01:01:33 GMT

The problem now is that directives are sorted according to a predetermined order, so respond 404 happens before all the reverse_proxy directives.

You can use handle blocks to set up mutually exclusive routing. For example:

proxy1.hooli.xyz {
	@google header Proxy-Backend google
	handle @google {
		reverse_proxy https://google.com
	}

	# Fallback if no other "handle" matches
	handle {
		respond 404
	}
}

Thanks. I got this working. For others interested my full Caddyfile is:

proxy1.hooli.xyz {
    log {
        output file /var/log/caddy/access.log
    }

    @google {
        header ReverseProxy-Upstream google
    }

    @yahoo {
        header ReverseProxy-Upstream yahoo
    }

    @espn {
        header ReverseProxy-Upstream espn
    }

    handle @google {
        reverse_proxy https://google.com {
            header_up Host {upstream_hostport}
            header_up X-Forwarded-Host {host}
            header_down -server
        }
    }

    handle @yahoo {
        reverse_proxy https://yahoo.com {
            header_up Host {upstream_hostport}
            header_up X-Forwarded-Host {host}
            header_down -server
       }
    }

    handle @espn {
        reverse_proxy https://espn.com {
            header_up Host {upstream_hostport}
            header_up X-Forwarded-Host {host}
            header_down -server
       }
    }

    handle {
         respond "reverse proxy upstream not found" 404 {
             close
         }
    }
}
1 Like

Good job. For the record, I think you could probably tidy up that config even more with the map directive:

This topic was automatically closed after 30 days. New replies are no longer allowed.