Path Matcher not Working on Internal Network Address accessed Directly with IP

1. Caddy version (caddy version):

v2.1.1 h1:X9k1+ehZPYYrSqBvf/ocUgdLSRIuiNiMo7CvyGUQKeA=

2. How I run Caddy:

a. System environment:

Raspberry Pi OS 05-27-2020 (previously called Raspbian) based on Debian 10 (Buster). I am running it on a Raspberry Pi. I followed the installation procedure on Caddy’s wesbsite.

I am not running on docker. I assume it is systemd given I used caddy.service inside /etc/systemd/system and use systemctl command.

b. Command:

sudo systemctl start caddy

c. Service/unit/compose file:

# caddy.service
# For using Caddy with a config file.
# Make sure the ExecStart and ExecReload commands are correct
# for your installation.
# See for instructions.
# WARNING: This service does not use the --resume flag, so if you
# use the API to make changes, they will be overwritten by the
# Caddyfile next time the service is restarted. If you intend to
# use Caddy's API to configure it, add the --resume flag to the
# `caddy run` command or use the caddy-api.service file instead.


ExecStart=/usr/local/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile


d. My complete Caddyfile or JSON config:

:80 {

  root /main/* /var/www/


3. The problem I’m having:

under /var/www I have a test HTML file. However, when I try to hit my IP from another computer on the same network with this: <Raspberry_Pi_IP_Address>/main it does not work. However, when I had my config file as follows below:

:80 {

  root * /var/www/


and in my browser I go to just the IP address http://<Raspberry_Pi_IP_Address>, it works correctly and the HTML file loads.

4. Error messages and/or full log output:

no particular error message. It just does not load

5. What I already tried:

I have tried different configurations for the Caddyfile. I mentioned using just root * above, but I also tried the following:

:80 {

  root /main /var/www/



<IP_ADDRESS>/main:80 {

  root * /var/www/


and other combinations

6. Links to relevant resources:

Please help me! thanks to all in advance!

Hi @nizar.t96, welcome to the Caddy community!

This is expected behaviour.

To explain a bit, whenever Caddy’s static file server gets a request, it takes the webroot, adds the URI, and that’s where it looks for the file to serve. Always.

That means when you set the root to /var/www/ and receive a request for /main, it will look for /var/www/main (or /var/www/main/index.html).

Using matchers does not alter the request in any way - think of them as only filters which tell Caddy what kinds of requests it should apply directives to. So, even if you do something like root /main /var/www/, and make a request for /main/, the file server is still going to look for /var/www/main/!

(And on top of that - now root doesn’t apply to requests that don’t start with /main, so… who knows where Caddy will be looking for those files? It’s not explicitly defined, so it’ll be the current working directory when you launch Caddy. Could be a security issue!)

So, if you want this to work, you’re going to need to do one of two things.

  1. Move everything inside /var/www/ into /var/www/main/. This’ll solve your problem pretty much straight away, by matching up the disk path to the URI.
  2. Strip the prefix /main from the URI before handling the request.

To do #2, you’d start out with something like this:

route /main* {
  uri strip_prefix /main

But by using route /main* we’re handling requests for /main, /main/foo… but also /mainbar etc which probably isn’t strictly accurate…

So this form is useful instead:

route /main/* {
  uri strip_prefix /main

And now we’re only handling stuff that’s specifically inside /main/ folder. But now we don’t handle requests for /main (no trailing slash)!

So we add a redir to make sure clients are visiting the right URL:

redir /main /main/
route /main/* {
  uri strip_prefix /main

And now, combined with root * /var/www, Caddy will serve a request to /main/foo.html directly out of /var/www/foo.html.


Thank you so much for the help! this makes a lot of sense and I really appreciate the help!

I have a follow-up question. My intention is to eventually host multiple things, and I thought the best way to organize it is through different paths. Since I am relying on just the IP for now, it would be something like
<IP>/cloud goes to my nextcloud instance
<IP>/rss goes to my rss reader

and so on.

Would the answer you give me apply to these things too, or is it strictly for file_server? I am assuming nextcloud will need fastcgi and my rss reader will need reverse_proxy. Would I still strip the URI similarly, or are there different approaches?

The answer does indeed apply to those things! For essentially the same reason, too (albeit not quite identical).

Just like file_server, the reverse_proxy does not alter the URI in any way in conjunction with matchers by themselves.

That means if you make your clients prefix requests for the RSS reader with /rss, when Caddy proxies it, it will proxy /rss upstream and your upstream server might get a bit confused if it’s not expecting that. Many upstream apps don’t!

Unfortunately, that’s not the only problem you’ll run into when reverse proxying, though. If your upstream server has links to static resources like stylesheets, images etc. you might run into what I’ve written about in the past as the “subfolder problem”. Have a gander:

1 Like

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