How to efficiently group "LAN" and "Internet" endpoints?

(this is a general Caddy V2 question and not a troubleshooting one so I omitted the template.).

I run caddy mainly as a proxy server to docker containers. Caddy itself is exposed on Internet (ports 80 for LE, and 443 for general access). It is programatically configured via the API.

Some of the containers are intended to be available from the LAN only. For this case I currently chain two remote_ip directives:

  • the first one on my LAN ranges, which in turn proxifies to the relevant container,
  • and the a second one (on which sends pack an empty page and a 200 status code.

This way requests coming from the LAN reach the container, and the ones from Internet an empty page.

Some of the containers are in contrast intended to be available from both the LAN and Internet. This case is easy as there is nothing to filter.

All of that works great.

I consider for a bunch of reasons to move away from the API-based configuration to a static one. This is not settled down yet but in any case I would like to learn more about how to tackle the problem below.

When moving to a static configuration, the second case bove (containers intended to be reachable both from the LAN and Internet) are easy:


It gets more verbose for a LAN-only host {

reverse_proxy @lan
respond @internet ""

  @internet {

  @lan {

My concern is that for 20 LAN-only containers I will end up with 20 almost similar blocks, with only two different pieces.

My question: is there a way to either:

  • template the configuration file (in which case I would have 20 tuples with the container name and an IP, and iterate over them)
  • or have “global” blocks for @lan and @internet which will at least remove these redundant parts (the respond will have to stay but OK))

Or, more generally speaking: is there a smart way to deal with quasi-identical entries which have a significant common block of directives (short of replicating them)

That’s the ideal usecase for snippets!

(commonMatchers) {
	@external {
		not remote_ip
	@local {
} {
	import commonMatchers
	reverse_proxy @local
	respond @external 403
} {
	import commonMatchers
	reverse_proxy @external
	respond @local 403

Note I changed your matcher to use not instead, because there’s no guarantee for the order in which things will be matched in this case. Safer to invert the matching.


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