Mastering Caddy Path Routing: Using handle_path to Fix 404 Errors in Subfolder Deployments

What will this help you do?

Learn how to troubleshoot 404 errors when deploying apps in subfolders with Caddy by understanding the difference between handle and handle_path path prefix stripping.

Who is it for?

Readers working through this problem and checking each step.

Before you start

Check the scope of this guide, then prepare the environment and materials it actually calls for.

On this page

Deploying applications or static assets in subfolders (such as /blog or /docs) is a common pattern for web architects. However, a frequent pitfall when configuring Caddy is encountering unexpected 404 Not Found errors. This happens because search engine crawlers and regular users request paths that include the subfolder prefix, but the upstream application or file server expects a relative path without that prefix.

This guide explains how to solve this issue by comparing handle and handle_path directives, focusing on path prefix stripping and practical configuration steps.

Prerequisites and Scope

  • A running server with Caddy installed and a valid Caddyfile.
  • An application or static site deployed under a non-root path prefix (e.g., /blog).
  • Scope is strictly limited to Caddyfile routing and path handling directives.

1. Problem Diagnosis: Why Subfolder Deployments Return 404

When you use a standard handle block in your Caddyfile, Caddy matches the incoming request path but does not modify the request URI.

For instance, if you configure a block for /blog/*, a request to https://example.com/blog/about retains the full URI /blog/about when passed to your upstream application or internal file handler. If your Node.js, Python, or static file directory only knows about /about, it will fail to match the route and return a 404 Not Found error. This broken link prevents search engines from properly indexing your subfolder content.

2. Core Mechanism: handle_path vs uri strip_prefix

To bridge this gap, Caddy provides the handle_path directive. According to the official documentation, handle_path works the same as the handle directive, but it implicitly uses `uri strip_prefix` to strip the matched path prefix from the request URI before passing it to inner blocks handle_path Caddy Documentation.

Key behavioral rules for handle_path:

  • It accepts a single path matcher directly in its definition.
  • You cannot use named matchers inside a handle_path declaration.
  • The path left after stripping always starts with `/`. For example, matching /prefix/* for a request to /prefix results in the internal path / handle_path Caddy Documentation.

3. Scenario One: Fixing file_server Static Asset 404s

When serving static files from a subfolder, combining file_server with the site's global root directive can lead to incorrect file lookups if paths aren't stripped. The file_server directive forms file paths by appending the request's URI path to the site's root path file_server Caddy Documentation.

If you deploy a static site in /var/www/blog under the /blog/ prefix, use handle_path to strip the prefix so the file server looks for /index.html instead of /blog/index.html inside your root folder:

caddyfile
example.com {
    handle_path /blog/* {
        root * /var/www/blog
        file_server
    }
}

Verifying the File Server

Run the following verification command:

bash
curl -I https://example.com/blog/index.html

Expected Result: HTTP status 200 OK, confirming that Caddy successfully mapped /blog/index.html to /var/www/blog/index.html.

4. Scenario Two: Fixing Reverse Proxy Application 404s

For upstream applications running behind Caddy (such as a backend service listening on port 8080), receiving /blog/api/users instead of /api/users often triggers framework-level routing failures.

Use handle_path to seamlessly strip the /blog prefix before forwarding the traffic:

caddyfile
example.com {
    handle_path /blog* {
        reverse_proxy localhost:8080
    }
}

Verifying the Reverse Proxy

Send a test request to the upstream application route:

bash
curl -i https://example.com/blog/status

Expected Result: The upstream application logs and response headers must show it received /status, not /blog/status. The HTTP status code should be 200 OK.

5. Configuration Comparison and Verification Steps

To ensure your routing behaves correctly, follow this step-by-step verification checklist:

  1. Draft the configuration using handle_path instead of handle for any non-root path matcher.
  2. Reload Caddy using caddy reload --config /etc/caddy/Caddyfile.
  3. Test with curl to inspect response headers and payload.
  4. Check upstream logs to verify that the prefix has been cleanly stripped and requests start with /.

6. Risks, Side Effects, and Rollback

While handle_path offers great convenience, be aware of its side effects:

  • Over-stripping risk: Because the directive automatically strips the matched prefix, writing overly broad matchers (like /*) will strip your entire path down to /, breaking multi-level routing.
  • Fallback option: If you need granular control over which parts of the URI are stripped or need to preserve specific query parameters alongside complex rewrites, fall back to a standard handle block paired with an explicit uri strip_prefix /custom-prefix directive.

Verifying Path Stripping and Static File Resolution

When troubleshooting 404s in subfolder deployments, it is critical to verify that the path prefix is being stripped correctly before the request reaches the static file server or reverse proxy. The handle_path directive implicitly uses uri strip_prefix to remove the matched path prefix, ensuring the remaining path always starts with /. For example, with handle_path /prefix*, a request for /prefix is handled with the path /. This behavior is distinct from handle, which requires explicit uri strip_prefix configuration. To validate this, you can inspect the request path received by your backend or file server. If you are serving static files, ensure that the file_server directive is correctly resolving the stripped path against the site root. Note that file_server enforces canonical URIs, issuing redirects for directories without trailing slashes or files with trailing slashes, unless an internal rewrite modifies the last path element. For a deeper understanding of how handle_path simplifies this process compared to manual prefix stripping, refer to the official documentation: Caddy handle_path directive. Additionally, if your 404 errors persist after verifying path stripping, consider reviewing upstream connection issues or body limits, as detailed in our guide on Fixing Caddy Reverse Proxy 404 and 502 Errors.

Sources

Next steps

Continue with the next useful task; you do not need to read everything at once.

  1. Fix Caddy Reverse Proxy 404 and 502 Errors: Upstream, Path, and Body Limits →
  2. How Caddy Automatic HTTPS Works and How to Troubleshoot It →
  3. Deploy Your First Static Website to a Linux Server with Caddy →
RESOURCES I USE · REFERRAL

Two services to compare when you are ready to launch

This is not an automated ranking, and neither service is necessary for everyone. These are services I use, with the use case and limitations kept visible.

Cloud server · Used for early projects

RainYun

A practical candidate for a website or small service. Choose by user region, configuration and measured workload rather than the lowest headline price.

Referral disclosure: these links contain my referral information. I may receive a platform benefit if you sign up or order, at no additional charge from AIOOS. Check the order page for current pricing, availability, regions and terms. Read the full affiliate disclosure