Caddyfile config for reverse proxy using domain subfolder

1. Caddy version (caddy version):

v2

2. How I run Caddy:

docker compose

a. System environment:

raspbian Bullseye

b. Command:



c. Service/unit/compose file:

caddy:
    container_name: caddy
    image: caddy:latest
    volumes:
    - "/home/pi/caddy/Caddyfile:/etc/caddy/Caddyfile"
    - "/home/pi/caddy/data:/data"
    environment:
    - "TZ=Europe/Rome"
    restart: always
    ports:
    - "80:80"
    - "443:443"
    - "11110:11110"
    - "111111:111111"

d. My complete Caddyfile or JSON config:

(https_header) {

  header {
    Strict-Transport-Security "max-age=31536000; includeSubdomains"
    X-XSS-Protection "1; mode=block"
    X-Content-Type-Options "nosniff"
    X-Frame-Options "SAMEORIGIN"
    Referrer-Policy "same-origin"
  }
}

https://labahouse.ddns.net { 
  respond "Saluti da casamia"
  file_server
}
https://labahouse.ddns.net:11110 { 
  import https_header
  reverse_proxy http://10.0.0.40:80
}
https://labahouse.ddns.net:11111 { 
  import https_header
  reverse_proxy http://10.0.0.5:80
}
https://labahouse.ddns.net:8123 { 
  import https_header
  reverse_proxy http://10.0.0.5:8123
}

https://labahouse.ddns.net/mv{ 
  import https_header
  reverse_proxy http://10.0.0.40:80/cloud
}

3. The problem I’m having:

Hi all

I ask your support because I’m trying - without success - to configure Caddy to reverse proxy the services inside may LAN.

I have a variable ip, so I registered a domain with NoIp.
I can not use the ports to connect to internal servers because from my office laptop all ports are closed, excluded 80 and 443.

I’d like to reach the different services that are (at this moment):

  1. Nextcloud in server “mv” IP=10.0.0.40
  2. Nexccloud in server “NUC” IP=10.0.0.5
  3. Home Assistant in server “NUC” IP=10.0.0.5

in this way:

  1. https//labahouse.ddns.net/mv ==> http//10.0.0.40/cloud
  2. https//abahouse.ddns.net/nuc ==> http//10.0.0.5/cloud
  3. https//labahouse.ddns.net/ha ==> http//10.0.0.5:8123

The only way I found to connect to the two different Nextcloud server is by reversing two ports. If I try using a subfolder caddy’s docker doesn’t start, and I get an error (at the bottom of this page)

  1. https//labahouse.ddns.net:11111/cloud ==> http//10.0.0.40/cloud
  2. https//abahouse.ddns.net:11110/cloud ==> http//10.0.0.5/cloud

4. Error messages and/or full log output:

{"level":"error","ts":1642976703.068634,"logger":"http.handlers.reverse_proxy","msg":"aborting
 with incomplete response","error":"context canceled"}
{"level":"info","ts":1642977453.4528205,"msg":"shutting down apps, then terminating","signal":
"SIGTERM"}
{"level":"warn","ts":1642977453.4533787,"msg":"exiting; byeee!! ď‘‹","signal":"SIGTERM"}
{"level":"info","ts":1642977454.6018977,"logger":"tls.cache.maintenance","msg":"stopped backgr
ound certificate maintenance","cache":"0x2c0b860"}
{"level":"info","ts":1642977454.6077657,"logger":"admin","msg":"stopped previous server","addr
ess":"tcp/localhost:2019"}
{"level":"info","ts":1642977454.6100597,"msg":"shutdown complete","signal":"SIGTERM","exit_cod
e":0}
{"level":"info","ts":1642977468.9532478,"msg":"using provided configuration","config_file":"/e
tc/caddy/Caddyfile","config_adapter":"caddyfile"}
run: adapting config using caddyfile: parsing caddyfile tokens for 'reverse_proxy': /etc/caddy
/Caddyfile:24 - Error during parsing: for now, URLs for proxy upstreams only support scheme, h
ost, and port components
{"level":"info","ts":1642977489.1823535,"msg":"using provided configuration","config_file":"/e
tc/caddy/Caddyfile","config_adapter":"caddyfile"}
run: adapting config using caddyfile: parsing caddyfile tokens for 'reverse_proxy': /etc/caddy
/Caddyfile:24 - Error during parsing: for now, URLs for proxy upstreams only support scheme, h
ost, and port components

5. What I already tried:

6. Links to relevant resources:

Which version, specifically? It matters (you might be on an older version with bugs, etc).

Run docker-compose exec caddy caddy version to find out.

You’re better off using subdomains for each service instead. Proxying to subpaths is tricky, and often not possible depending on how the upstream app is implemented or configured. Just make sure *.labahouse.ddns.net also resolves to your server’s IP address as well.

But your Caddyfile has three issues:

First, you need to make sure you have a space the site address and the {, otherwise Caddy will parse the { as being part of the address, which doesn’t make sense.

Second, path matching in Caddy is exact, so if you try to use a path matcher like /mv, it will only match requests to exactly /mv and not /mv/foo. You need to use a * to tell Caddy to match more, i.e. /mv*. But in general, I strongly suggest not using path matchers in the site address, and to instead use the handle directive in a single site block if you need to make separate routes. For example:

https://labahouse.ddns.net {
	handle /mv* {
		reverse_proxy 10.0.0.40:80
	}

	handle {
		respond "Saluti da casamia"
	}
}

Third, Caddy doesn’t support paths in the reverse_proxy upstream address, because that implies a simultaneous rewrite. If you need to rewrite the request path, then use the rewrite directive to perform it before proxying.

Hi Francis,

first of all, thanks a lot for your suggestions.

This is the result of query for version: “CADDY_VERSION=v2.4.6”

Yep, I didn’t notice the lack of space: in the caddyfile it’s correct, I deleted it when rewriting that part of code.

So, if I understand well, it’s better to use a link like

https://mv.labahouse.ddns.net

I just activated the wildcard for CNAME, so I can use all the different prefix I need (and I don’t have to manually renew the redirect every month).

Now I’ll check documentation to understand the correct way to write the rule.

About the SSL certificate: it is correct to disable the ssl on each server inside lan, redirecting the traffic from proxy to server? Because the ssl is handled by caddy and it’s valid for all subdomains (I had a very quick try with Home Assistant but I got an error, because in that installation I already acttivated an SSL certifiate with Duckdns - unfortunately blacklisted by my company’s proxy -)

Thank you!

Yes, that’s the usual way to do it. See this article for a deeper explanation:

1 Like

I’m studying to understand how to write the rule for using wildcards, but after a quick and unsuccessfull attempt yesterday night I have a doubt.

If I use the wildcard, do I need a Caddy + specific plugin (I use NoIp, that I suspect is not in the supported list), or do I will fail?
After a quick search, I haven’t found any docker container including this plugin.

Why do you need a wildcard cert? That’s usually not necessary, if you know all the domains you need ahead of time. Caddy can issue as many certs as you need, one per domain.

You would, yes, because ACME requires use of the DNS challenge to prove that you control the entire zone.

I don’t know if there’s a noip DNS plugin for Caddy. But if there is, you’d need to build the docker image yourself by using a Dockerfile. See the docs on Docker, specifically the section “Adding custom Caddy modules”.

I’m thinking about wildcard as alternative way instead of using path.

I tried to follow your suggestion, using the handle and rewrite directive but I get error from browser (Bad Request: your browser sent a request that this server could not understand)

I configured Caddyfile like this, to redirect requests to the correct server (trying to access to a simple html file)

(https_header) {
  header {
    Strict-Transport-Security "max-age=31536000; includeSubdomains"
    X-XSS-Protection "1; mode=block"
    X-Content-Type-Options "nosniff"
    X-Frame-Options "SAMEORIGIN"
    Referrer-Policy "same-origin"
  }
}


https://labahouse.ddns.net {
        handle /mv* {
#                import https_header
                rewrite /mv* *
                reverse_proxy 10.0.0.40:80
        }

        handle {
                respond "Saluti da casamia"
        }
}

If I can get everything working I’ll avoid to start more complex configuration with plugins and so on :slight_smile:

What are you trying to do with this rewrite? This isn’t valid. This will literally rewrite all requests to have the path *, which will break all requests.

If you’re trying to strip the /mv prefix from the path, then you should use handle_path instead of handle.

But really, see this article, that may not work depending on your upstream application:

Hi Francois, thank for your support.
I realise that I have to understand a bit better how “www world” works, in order to set correcly the rules, and follow your suggestions. So I won’t bother with too many and obvious questions :slight_smile:
In case, I will come back asking

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