Serve and route requests
Strip a path prefix
Choose handlepath when an upstream or file tree should receive a path without its matched prefix.
10 minute lesson
Sometimes you mount something under a path prefix — docs under /docs/, an API under /api/ — but the thing being served doesn’t know about that prefix. A directory of HTML files has no docs/ folder inside it. An upstream app expects /users, not /api/users.
handle_path solves exactly this. It works like handle, with one addition: it strips the matched prefix from the path before running its handlers.
:8080 {
handle_path /docs/* {
root * ./manual
file_server
}
}
Create a file and test it:
mkdir -p manual
echo '<h1>Getting started</h1>' > manual/start.html
curl http://127.0.0.1:8080/docs/start.html
The request path is /docs/start.html. Inside the block, the path becomes /start.html, so file_server looks for manual/start.html. Without the stripping it would look for manual/docs/start.html and return a 404 even though your file is right there.
The same logic applies to proxying:
:8080 {
handle_path /api/* {
reverse_proxy 127.0.0.1:3000
}
}
The upstream receives /users, not /api/users. The prefix stays your routing concern; the app never learns it exists.
Choose deliberately between the two directives. Use handle when the downstream expects the full original path. Use handle_path when the prefix is purely a mounting point. Getting this wrong produces 404s that are confusing to debug, because the path Caddy looks up is not the path in your browser. When in doubt, check what actually arrived: for a file server read the runtime log, for a proxy log the request path inside your application.
And resist the temptation to pile up rewrite rules until a request happens to work. handle_path states your intent in one word. A stack of rewrites hides it.
Lesson completed