网站目录里的备份就是源码泄漏
动一个大文件之前,很自然的一步是先复制一份。cp index.php index.php.改之前,然后开工。如果这个文件躺在 Web 服务器对外发布的目录里,你刚刚把自己的全部源代码变成了可以当纯文本下载的东西——数据库凭据、密钥,文件里碰巧有的一切。
Apache 让 index.php 走 PHP,是因为它以 .php 结尾。index.php.改之前 不以 .php 结尾。它以 .改之前 结尾,而 Apache 从没听说过这个后缀,于是它做那件默认的事:把字节发出去。
大多数人写的那条规则,以及它为什么不够
通常的防护是一张禁止扩展名的清单:.bak、.orig、.save、.sql、.old。它能拦住你想到的那个文件。在这里实测过,这张清单对 probe.bak 给出了正确答案,而对下面每一个都给错了:
index.php.vor-xyz—— 事情就是这么发生的index.php.2026-08-19index.php.kopiestyle.css.altindex.php.1
全部以 200 送出。你没想到的那种命名方式,恰恰就是你会用的那种,因为备份的名字是按你当时正在做什么起的。
把切口反过来
真正管用的规则不问哪些扩展名被禁止。它问一个关于结构的问题:这个文件名里,是否含有一个不在末尾的源码扩展名?
如果有,那它就是某个本该被执行的东西的副本,不管后面跟的后缀说什么。index.php.随便什么 命中。style.css.随便什么 命中。这个模式覆盖了还没有人发明出来的名字,而这正是全部要点。
今天对着运行中的服务器验过:上面五个名字现在都返回 403,包括那些根本不存在的——这条规则拒绝的是形状,不是文件。
那个例外,以及它教了什么
这里有一个正当的文件名恰好就是这个形状:预压缩资源写成 style.css.1787777226.gz,扩展名在中间,是设计如此。所以规则带了唯一一条很窄的例外,针对「一段数字后面跟 .gz」,而这个文件如愿返回 200。
这类规则通常正是在这样的例外上塌掉的,因此值得说清楚是什么让这一条安全:它窄到你没法拼出 index.php.某某.gz 把源码拿回去,因为中间那一段必须全是数字。更宽的例外——「任何以 .gz 结尾的东西」——会把整条规则原样奉还。
而最简单的建议依然是那条让这一切都变得不必要的:把备份放在发布目录之外。规则是为你忘记的那一天准备的一张网,不是一张可以从此不再在意副本落在哪里的许可证。