The part where .htaccess
gets stickest is when there are multiple .htaccess
files, such as one in the webroot, one in the first subdirectory, etc…
Luckily all the .htaccess
modification happens at the root level. Apache rewriting happens in a series of steps; understanding each step and what it’s trying to achieve makes it quite easy to achieve the same results in Caddyfile config. This means that:
Converting the .htaccess
file and figuring out how to achieve the objectives are, more or less, the same thing.
It might appear so, but it’s pretty straightforward. The step-by-step nature helps. I’ll roll through one by one and we’ll figure out what’s happening as we go:
<IfModule mod_rewrite.c>
RewriteEngine on
#...snip
</IfModule>
This part just tells Apache to enable rewriting, assuming the rewrite module is enabled. Irrelevant to Caddy; rewriting is core functionality.
RewriteCond %{HTTP_HOST} ^(www.)?example.com$
As a precondition for rewrites, the requested host (%{HTTP_HOST}
) must be the specified hostname (^(www.)?example.com$
, regex for example.com
with or without a www
subdomain). We don’t need this one at all - since Caddyfile configuration hinges primarily on the site label, when we write our own rewrites, we already know for sure the client is requesting the right host.
RewriteCond %{REQUEST_URI} !^/my_subdir/
As a precondition for rewriting, this first checks that the URI doesn’t begin with /my_subdir/
. In Caddy, we do that with a matcher, like not path /my_subdir/*
.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
These parts check that the requested URI is not a file that exists on disk (!-f
, a.k.a. “not -f
”) and that it’s also not a directory on disk (!-d
, “not -d
”).
Once again, Caddy has a matcher to check this: not file
. Easy.
RewriteRule ^(.*)$ /my_subdir/$1
Here’s the actual meat of the rewrite. The first part (^(.*)$
, regex for “literally anything”) captures the original URI. The second part (/my_subdir/$1
) takes the captured URI and puts it after the subdir, essentially prefixing it.
Doing this kind of prefixing in Caddy is also ludicously easy; it’s just a rewrite <matcher> /prefix{uri}
. We also get to skip using regex entirely, keeping things simpler (and slightly faster!).
RewriteCond %{HTTP_HOST} ^(www.)?example.com$
RewriteRule ^(/)?$ my_subdir/index.php [L]
Now we’ve got a second rewrite happening that double checks that we’re still on the right host (Caddy still doesn’t need to check this), then rewrites from /
to /index.php
(we don’t need to do this either, since php_fastcgi
actually checks for this automatically!). We can just throw this part out.
So. Combine our matchers and add the rewrite:
@subdir {
not path /my_subdir/*
not file
}
rewrite @subdir /my_subdir{uri}
…Looks a fair bit simpler than Apache, doesn’t it?