Just to clarify, you're not actually changing the document root here, you are effectively hiding a subdirectory using URL rewriting. To actually change the document root, you need to change the DocumentRoot
directive in the server config (as mentioned in comments) - which will have other implications (your node app is probably dependent on the DocumentRoot
being set as it is).
RewriteCond %{REQUEST_URI} !public/
RewriteRule (.*) /public/$1 [L]
This checks for the absence of "public/" anywhere inside the requested URL-path, which is not really what you require. However, the RewriteCond
directive is not necessary here. You should instead be checking the URL-path in the RewriteRule
pattern. For example:
RewriteRule !^public/ /public%{REQUEST_URI} [L]
Note that the REQUEST_URI
server variable includes the slash prefix. (Which maybe why you removed the anchor in your CondPattern in order to get it to "work"?)
I've tried adding RewriteRule ^public/ - [L,R=404]
to the end, but end up returning 404 for everything.
That's the right idea, however, by itself this will also redirect rewritten URLs (by the other directive). You need to only target direct requests. There are several ways to do this. My preference is to check the REDIRECT_STATUS
environment variable, which is empty initially and set to "200" after the first successful rewrite. For example:
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^public($|/) - [R=404]
($|/)
- Note that I've also made the trailing slash "optional", in that it will match either public
or public/
(using alternation) when accessing the bare directory. This is to avoid mod_dir from first issuing a 301 redirect to append the trailing slash on the directory, before sending a 404 in response to the redirected (2nd) request. The alternation (end-of-string or slash) ensures that public/<foo>
is matched, but not public<foo>
, should you have URLs that start "public" (eg. /public-enemy
).
There is no need for the L
flag when using a non 3xx (or 2xx) status code as it is implied.
In summary:
# Block direct requests to the "public" subdirectory
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^public($|/) - [R=404]
# Rewrite all requests that are not already for the "public" subdirectory
RewriteRule !^public/ /public%{REQUEST_URI} [L]
Note that the blocking (404) directive should be first.