Caddy reverse proxy multiple port automatically

https://example.com {
  tls cert.crt private.key
  redir /api1 /api1/
  handle_path /api1/* {
    reverse_proxy 1.1.1.1:8000
  }
  redir /api2 /api2/
  handle_path /api2/* {
    reverse_proxy 1.1.1.1:8001
  }
}

this is a caddyfile. i can access https://example.com/api1 to get service from port 8000 and access api2 to get service from port 8001. If i want to add another service, i must change the caddyfile and add an api3 and the port of new service. can caddyfile do these thing automatically? for example, below is what i want.
https://example.com/{{port}} => 1.1.1.1:{{port}}
the {{port}} is a variable. it could be 8001, 8002 or any othor legal port.
it sounds a little crazy. can caddy do it?

i can’t find solution in doc. Can anyone tell me how to do or just caddy can’t do it?

There’s not really any way for Caddy to automatically enumerate arbitrary lengths of linear apiX → port number relationships. That’s a job for a good text editor or a templating engine for your configuration.

However, I don’t recall off the top of my head if you can placeholder the port in an upstream, but it might be doable because we should be able to lift the API number from the path with a regexp matcher and just slap it on the end of the port. It won’t be very intelligent at all, though… And it might have security implications…

But I’m imagining something like this maybe?

example.com {
  tls cert.crt private.key

  # Handle all trailing slash appending on /apiXYZ
  @slash path_regexp slash ^/api([0-9]+)$
  redir @slash /api{re.slash.1}/

  # Grab the API number and slap it on the upstream port
  @api path_regexp api ^/api([0-9]+)/.*$
  handle @api {
    uri strip_prefix /api{re.api.1}/
    reverse_proxy 1.1.1.1:800{re.api.1}
  }
}

This would produce an /apiXY/*1.1.1.1:800XY relationship. This works fine for 1-9 but at 10 you’ll find the port jumps to 80010, for example, so it’s not ideal; it’s simple string concatenation, not actually properly incrementing the port number.

I haven’t tested this at all, I’m just spitballing; cc @francislavoie to indicate if this approach is viable?

As an alternative if this isn’t workable, to keep your config more DRY, you could put your handlers in a snippet; something like this, also untested.

(api) {
  redir /api{args.0} /api{args.0}/
  handle_path /api{args.0}/* {
    reverse_proxy 1.1.1.1:{args.1}
  }
}

example.com {
  tls cert.crt private.key
  import api 1 8001
  import api 2 8002
  import api 3 8003
  # ...etc
}

See:

This would probably be best implemented with a dynamic upstreams module. You’d have to write a bit of Go code but it should be quite easy to implement that way.

I’m on vacation still right now but @matt could fill in the details if you’re willing to try that.

1 Like

it works after deleting the last ‘/’. thanks! I didn’t think it could be used this way before. i think if caddy docs add some slightly more complex usage example, it would be easier to understand how to use it.