Serving two static sites with different routes

I have this very simple approach and it is not working.
It serves the last handle but not the /api/* handle.

I tried so many differnt ways, I am really loosing hope into Caddy right now.

v2.gouna.tech {
        handle /api/* {
                root * /srv
                file_server
        }
        handle {
                root * /srv2
                file_server
        }
}

Something like thi isn’t working as well:

v2.example.tech {
                root * /srv
                file_server
}
v2.example.tech/api {
                root * /srv2
                file_server
}

I guess I am missing the concept here.

In Caddy, even though it matched the path, it doesn’t mean it’ll strip it as it passes the request down. In other words, requests for /api/foo cause Caddy to search for file in /srv/api/foo. You’re probably expecting it to look for /srv/foo.

The fix is to strip the path:

v2.gouna.tech {
-        handle /api/* {
+        handle_path /api/* {
                root * /srv
                file_server
        }
        handle {
                root * /srv2
                file_server
        }
}

Next time, please don’t delete the template. It helps both of us to figure out the issue. Logs would be helpful here.

3 Likes

Hi @Mohammed90,
this works very well, thanks for the help. I was missing the link between the path and the File_server handling.

Actually I would think the same strategy would apply for a reverse proxy but it turns out not to work.


{
        debug
}
v2.gouna.tech {
        handle_path /pga/* {
                reverse_proxy docker-pgadmin-1:5555
        }
        handle_path /api {
                root * /srv
                file_server
        }
        handle {
                root * /srv2
                file_server
        }
}

Tried different combinations of “/pga” "/pga/* “handle” and “handle_path” but again no luck.

Just getting this Log:
caddy-1 | {"level":"debug","ts":1727068372.8061554,"logger":"http.log.error","msg":"{id=dvxnxe2my} fileserver.(*FileServer).notFound (staticfiles.go:651): HTTP 404","request":{"remote_ip":"91.58.118.32","remote_port":"54278","client_ip":"91.58.118.32","proto":"HTTP/2.0","method":"GET","host":"v2.gouna.tech","uri":"/pga","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"128\", \"Not;A=Brand\";v=\"24\", \"Google Chrome\";v=\"128\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"],"Sec-Fetch-Site":["none"],"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-Dest":["document"],"Priority":["u=0, i"],"Sec-Ch-Ua-Mobile":["?0"],"Upgrade-Insecure-Requests":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"],"Sec-Fetch-User":["?1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-US,en;q=0.9"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"v2.gouna.tech"}},"duration":0.000136856,"status":404,"err_id":"dvxnxe2my","err_trace":"fileserver.(*FileServer).notFound (staticfiles.go:651)"}

These won’t match. Your handle_path requires at least /pga/ but you only requested /pga for this log entry.

/pga != /pga/*

For a request to URI /pga, a handle_path /pga would’ve caught it. (It would not work for anything longer like /pga/ or /pga/foo, though, so we typically expect this not to be the behaviour users want.)

~/Projects/caddy
➜ cat Caddyfile
localhost {
  handle_path /foo {
    respond "I'm /foo"
  }
  respond "I'm not /foo"
}

~/Projects/caddy
➜ curl https://localhost/foo
I'm /foo⏎

~/Projects/caddy
➜ curl https://localhost/foo/bar
I'm not /foo⏎

The magic matcher you probably missed is /pga*.

~/Projects/caddy
➜ cat Caddyfile
localhost {
  handle_path /foo* {
    respond "I'm /foo"
  }
  respond "I'm not /foo"
}

~/Projects/caddy
➜ curl https://localhost/foo
I'm /foo⏎

~/Projects/caddy
➜ curl https://localhost/foo/bar
I'm /foo⏎
1 Like

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