This episode covers URI manipulation: the rewrite and uri directives, stripping and adding prefixes, clean URLs, variables and placeholders, regex capture groups, and try_files patterns for SPA and PHP routing.

Not every incoming URL is perfect. Users open /produk while the file is named /produk.html. An old API uses /v1 while the new one uses /v2. An SPA routes everything through index.html. All of this is handled with URI manipulation — and episode 12 is where you learn it.
rewrite changes the path that handlers process, without changing the URL in the browser:
example.com {
rewrite /produk /produk.html
root * /var/www
file_server
}A request to /produk is changed to /produk.html internally before file_server looks for the file. The browser still shows /produk in the address bar — clean for SEO.
Removing a prefix is a common reverse proxy pattern:
example.com {
rewrite /api/* /api{path}
reverse_proxy localhost:8080
}Actually, the more common pattern is stripping the prefix before reaching the backend. Example using regex:
example.com {
@api path /api/*
handle @api {
uri strip_prefix /api
reverse_proxy localhost:8080
}
}uri strip_prefix /api removes /api from the path. So /api/users becomes /users when forwarded to the backend — the backend doesn't need to know about the prefix.
uri can add, change, or remove query parameters:
example.com {
@tracked query campaign=*
handle @tracked {
uri query_upd utm_source caddy
reverse_proxy localhost:8080
}
}uri query_upd utm_source caddy adds or updates the utm_source=caddy parameter. Useful for campaign tracking before the request is forwarded.
uri can also normalize paths — for example, cleaning up double dots:
example.com {
uri path_clean
root * /var/www
file_server
}uri path_clean normalizes segments like /a/../b to /b. This prevents path traversal.
Caddy provides placeholders you can use in directives:
*.example.com {
root * /var/www/sites/{host}
file_server
}Common placeholders you should know:
{host} — the host from the request.{path} — the path without the query.{query} — the query string.{method} — the request method.{remote_host} — the client IP.root * /var/www/sites/{host} uses the {host} placeholder to pick a directory per subdomain — a static multi-tenant pattern.
The path_regexp matcher can capture parts of the path and reuse them:
example.com {
@artikel path_regexp ^/blog/([a-z0-9-]+)
handle @artikel {
rewrite /blog/{re.1} /posts/{re.1}.html
root * /var/www
file_server
}
}rewrite /blog/{re.1} /posts/{re.1}.html uses {re.1}, the first capture group from the regex — the article name captured from the URL. This pattern maps clean URLs to hidden physical files.
The most common pattern for React and Vue applications:
app.example.com {
root * /app/dist
try_files {path} /index.html
file_server
}try_files {path} /index.html tries the file matching the path; if it doesn't exist, it falls back to index.html so the SPA router works. We touched this in episode 5 — here you see the reason from the URI side.
Combine the fallback with a dedicated page:
example.com {
root * /var/www
try_files {path} {path}.html /404.html
file_server
}If neither the file nor the .html version exists, Caddy serves 404.html with status 404. For PHP, the pattern becomes:
example.com {
root * /var/www
php_fastcgi unix//run/php/php-fpm.sock
try_files {path} index.php?{query}
}try_files {path} index.php?{query} routes every request that doesn't match a file to index.php — the front controller pattern used by Laravel and Symfony.
Episode 12 completed your routing arsenal: rewrite for internal path changes, uri for prefix stripping and query manipulation, variables and placeholders like {host}, {path}, and {query}, regex capture groups with {re.1}, and try_files patterns for SPAs, 404 pages, and PHP front controllers.
Key takeaways:
rewrite changes the internal path without changing the browser URL.uri strip_prefix removes a prefix before forwarding to the backend.{host}, {path}, {query} are available throughout the configuration.{re.1}.try_files {path} /index.html is the standard SPA pattern.try_files {path} index.php?{query}.In the next episode, episode 13, we'll cover redirects & error handling — the redir directive with all its status codes, common patterns like www to non-www and old URL redirects, custom error pages with handle_errors, and the respond directive for static and JSON responses.