Your .htaccess Does Nothing on a Synology NAS
I spent an evening convinced I had a syntax error. I’d dropped an .htaccess into a folder on my Synology NAS to keep a config directory from being served, reloaded, and the file came back exactly as before. No error in the logs. No effect at all.
# This file is being completely ignored
<Files "config.php">
Require all denied
</Files>
The rules weren’t wrong. They were never read.
What’s actually happening
Synology’s Web Station serves sites through nginx. .htaccess is an Apache feature — specifically, it’s Apache’s mechanism for reading per-directory config at request time. nginx has no equivalent and never will; the maintainers consider per-directory config files a performance mistake by design. There is no module to enable, no setting to flip.
So the file just sits there. And because it sits there looking like a security control, it’s worse than having nothing: you’ll remember that you protected the folder.
You can switch a Web Station virtual host to Apache in DSM, but on a NAS that also runs DSM’s own services, I found that trading one set of surprises for another. The better move is to stop needing the file.
The fix
Anything you were going to put in .htaccess has to move to a place that actually runs.
Access control moves into PHP. Put the guard at the top of every entry point, not in a config file:
<?php
// guard.php — required as the first line of every page and API entry
function ip_gate() {
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
$ok = str_starts_with_compat($ip, '172.16.') // internal LAN
|| preg_match('/^100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\./', $ip); // VPN range
if (!$ok) { http_response_code(403); exit('forbidden'); }
}
Files that should never be served get moved, not blocked. The reliable version of “deny access to config.php” is putting config.php outside the web root entirely and including it by absolute path. A file that isn’t under the served directory cannot be requested, no matter which web server is running.
Real rewrites go into the nginx config. DSM lets you add server-level configuration, but treat this as a last resort: it survives reboots and not always upgrades, and it’s the piece most likely to be silently dropped when you least want it.
How to confirm it
Don’t confirm by reading your rules. Confirm by requesting the thing you think you blocked:
curl -s -o /dev/null -w '%{http_code}\n' https://your-nas/app/config.php
403 means the guard ran. 200 means you’re serving your database password to anyone who guesses the filename, which is what I was doing while feeling protected.
One more surprise in the same family
While you’re here: after you create a new .php file on a Synology share, it may 404 for a while even though it exists on disk. nginx and PHP-FPM cache directory state, and SMB writes don’t always trigger an immediate refresh. If a file you just created returns 404, wait, or touch the parent directory, before you start debugging code that is fine.
The general lesson I took away is narrower than “know your web server.” It’s that a config file that has no effect fails silently by design — it produces no error, because as far as the running system is concerned, you never asked for anything. The only way to find out is to test the behavior instead of reviewing the file.
Comments
Loading comments…