try_files $uri $uri/ =404 本身不主动拦截目录遍历,但配合严格 root 设置可大幅降低风险:它直接拼接并检查物理路径,不解析 ../,不存在即返404。

直接用 try_files $uri $uri/ =404 本身不能主动拦截目录遍历,但它能大幅降低风险——前提是配合正确的 root 设置和路径约束逻辑。
核心原理:不解析、不拼接、只查物理路径
这条指令的执行逻辑是:
-
$uri:Nginx 拿请求路径(如/uploads/../etc/passwd)直接拼到root后,得到完整物理路径(如/var/www/uploads/../etc/passwd) - 然后真实去磁盘检查该路径是否存在文件或目录
- 不存在就立即返回 404,不会尝试“规范化路径”或“向上跳转”
- 也就是说,
../不会被自动解析成父目录,只是作为路径字符串的一部分参与查找——而真实系统中几乎不可能存在名为..的子目录
必须搭配 root 定义与路径隔离
如果 root 指向的是网站根目录(如 /var/www/html),且没有额外开放别名或软链,那么即使请求 /../../etc/passwd,拼出的路径是 /var/www/html/../../etc/passwd,操作系统会按字面查找,结果必然是 404。
关键点:
- 确保
root是严格限定的静态目录,不指向/或用户主目录等宽泛路径 - 避免使用
alias替代root时路径计算错误(alias会替换整个匹配段,易导致越界) - 不要在
location /外再写全局root,防止被意外覆盖
需额外加固的边界情况
仅靠 try_files $uri $uri/ =404 还不够,因为 Nginx 在某些场景下仍可能暴露结构:
- 若开启
autoindex on,访问目录且无索引页时会列出文件——必须禁用 - 若 location 匹配了正则(如
location ~ \.php$),恶意请求可能绕过 try_files 直接进 PHP 执行——应前置拦截规则 - URL 编码的
%2e%2e%2f可能绕过简单正则过滤——建议加一层if ($request_uri ~ "(?:\.\./|%00|//)") { return 403; } - 敏感路径如
/.env、/wp-config.php应单独用location ~ /\.|\.env|\.log$显式拒绝
对比其他方案更可靠的原因
相比 deny all 或正则拦截 ../,try_files $uri $uri/ =404 更底层、更少依赖规则完整性:
- 它不依赖对攻击特征的识别,而是依靠操作系统真实的路径存在性判断
- 不引入重写或跳转,无额外执行路径,性能开销极小
- 即使攻击者用非常规编码或嵌套方式构造路径,只要最终拼出的物理路径不存在,就安全返回 404










