At one point I thought I had found an easy way to limit access to certain web root directories on the server level using mod_rewrite with Apache. The problem usually is that once you throw a 404 on specific pages, users will be presented with the Apache's default 404 page instead of the one from your CMS. For some time I thought the following was working to show the intended CMS 404 message:
RewriteRule ^res/ / [L] RewriteRule ^data/ / [L]
While investigating another unrelated issue I discovered that it was no longer working, if ever, and to my horror users could now request files located inside certain unintended subdirectories I thought I had 404'ed out. This was a surprise because I had already tested this at length in the past. Maybe I have later changed the code and forgotten about it, or something changed with a server update. Either way, one thing was sure: it was not working.
I remember another variation of this trick that looked something like this:
RewriteRule ^res/ /?404 [L] RewriteRule ^data/ /?404 [L]
This also did not work when I tried it. The idea was to rewrite the directories you want to block to a non-existent CMS-served-URL, which would then show the CMS' 404 message.
Fundamentally, however, I was thinking of using the wrong tool for the job; I was already directing all traffic through index.php, even static files. Some advantages to doing this:
- Better access control on static files, allowing to limit access to specific logged-in users – yes this is slower, but not significantly so.
- Easier to maintain from a central place in the PHP Router code.
- You spend less time fiddling with the server configuration.
Apache's configuration tend to call for weird syntax compared with using a regular expression in your PHP router to match URLs – the solution is therefor simple: move this logic to the Router code!
The router should support supplying a list of directories to exclude, and then, whenever something within that directory is requested, show a 404 message to the user. This list should be easily modifiable from a central location like a configuration file and/or certain patterns hard-coded inside the router.
Another solution is to move your CMS files outside of the public folder. Shopware does that by having a folder like /var/www/shopware/public/ while placing system files in /var/www/shopware/ – which is more practical? None. But you probably want your traffic through index.php regardless – at least for files you intend to control access to.
This does not mean you should not configure things on the server-level. It is still the fastest way to handle static file requests – but if you can, don't be afraid to do both, because one does not have to exclude the other.