How to "merge" these two matchers?

1. The problem I’m having:

As shown in the Caddyfile below, I support receiving requests on two different hostnames. On one hostname, I wish to rewrite the path of the request, but otherwise proxy it just like the other hostname. On the second hostname, I have a path whitelist but otherwise I want to proxy it just like the other hostname.
I have a working Caddyfile, but I currently need two different reverse_proxy blocks which I find undesirable as I need to make changes in both places. Part of the complexity probably comes from having the header extraction regex which is used the SRV lookup.

I’m curious if there’s some way to structure this such that I can have just one regex + reverse_proxy block that is only invoked if the OR of both sets of named matchers is satisfied.

It’ll make more sense after reading the Caddyfile below, but basically I want to do the following:

  • Is the request for *.domain.site? If so, prepend /http to the path.
  • Is the request for *.domain.cloud? If so, ensure it matches the path whitelist
  • If one of the above is true, regex extract ^([a-zA-Z0-9-]+)\.domain\.(cloud|site)$ from the Host header, and use it for the SRV lookup.

Right now, the last step is duplicated as two separate regex extractions + two separate reverse_proxy blocks.

2. Error messages and/or full log output:

No errors; just asking about a theoretical improvement.

3. Caddy version:

v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A=

4. How I installed and ran Caddy:

Running via homebrew on MacOS and via Docker on Ubuntu Focal LTS

b. Command:

caddy run --config ./Caddyfile

d. My complete Caddy config:

:8000 {
	@site-domain {
		header_regexp backend Host ^([a-zA-Z0-9-]+)\.domain\.site$
		host *.domain.site
	}
        # Prepend `/http` to the path on this domain
	uri @site-domain path_regexp /.* /http/$0
	@cloud-domain {
		header_regexp backend Host ^([a-zA-Z0-9-]+)\.domain\.cloud$
		path /api/* /version
	}
	reverse_proxy @cloud-domain {
		dynamic srv {
			name my-prefix-{re.backend.1}.service.consul
			refresh 1s
			resolvers 127.0.0.1:8600
		}
		transport http {
			resolvers 127.0.0.1:8600
		}
	}
	reverse_proxy @site-domain {
		dynamic srv {
			name my-prefix-{re.backend.1}.service.consul
			refresh 1s
			resolvers 127.0.0.1:8600
		}
		transport http {
			resolvers 127.0.0.1:8600
		}
	}
}

You’ll need to use an expression matcher, I think.

@domain `
	header_regexp('backend', 'Host', '^([a-zA-Z0-9-]+)\.domain\.(cloud|site)$') &&
	({re.backend.2} != 'cloud' || path('/api/*', '/version'))
`
handle @domain {
	@site `{re.backend.2} == 'site'`
	rewrite @site /http{uri}

	reverse_proxy {
		...
	}
}

I think this does what you want?

Thanks, appreciate the reply!
That should work, though for readability I might be better off just trying to put my reverse_proxy bits in a snippet (need to figure out how exactly the regex capture will interact)

Our path whitelist is quite long in reality, so trying to fit all of it into an expression will probably make it more confusing than it’s worth.

You could write it out like this:

@match <<CEL
	header_regexp(...)
	&& (
		{re.backend.2} != 'cloud'
		|| path(
			'/foo*',
			'/bar*'
		)
	)
	CEL

But anyway, you could use a named route + invoke for your proxy, so it’s the same actual reverse_proxy instance in memory with a shared cache etc.

1 Like

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