返回博客
2025年10月27日Sergei Solod5 分钟阅读

我的静态 Next.js 站点每天收到数千次漏洞扫描,但 Nginx 几乎没什么感觉

日志里充满了寻找 WordPress、PHP 后门、.env 和 .git/config 的探测请求。在我的静态 Next.js + Nginx 架构中,它们大多只会变成低成本的 miss 和 404,观察期间 CPU 负载也没有变化。这里真正值得讨论的是:这说明了静态架构的什么优势,又不能证明什么。

安全Next.jsNginxDevOps静态ホスティング攻击面

我的服务器日志每天都会记录数千次自动化漏洞扫描。单看这些 request,确实很吓人:

  • /wp-admin/wp.php
  • /111.php
  • /code.php
  • /.git/config
  • /i.php
  • /admin
  • /api/.env
  • /kal.php

真正让我意外的不是数量,而是服务器的反应。在我观察这些流量时,CPU 负载没有出现可见变化。

这个观察本身是真实的,但我最初的解释过于绝对。静态交付不是魔法护盾,404 也不是零成本。我的架构真正占优势的地方更具体:大量 opportunistic probe 的处理成本很低,因为 public request path 中几乎没有可供它们触达的 server-side application logic。

这些是探测,不是已经被入侵的证据

看到 /wp-admin/wp.php/111.php,很容易把每个 request 都叫作“exploit”。严格来说这并不准确。我主要看到的是 automated probing:bot 尝试常见文件名和 endpoint,然后观察服务器会返回什么。

有些 path 明显针对 WordPress 或 PHP。/.git/config/api/.env 这类请求则是在寻找被误公开的配置或 secret。/admin 更加泛化,甚至可能本来就是合法 route。可疑请求只能说明 scanner 在找什么,不能证明对应 vulnerability 真的存在。

在我的场景里,关键是架构:公开站点是 Next.js 预渲染出来的静态 HTML,由 Nginx 直接提供。

为什么这些流量在我的 stack 上成本很低

对于这里讨论的页面,并不是每个 request 背后都有 PHP runtime 或 database。Nginx 只是提供已经存在的文件。如果 scanner 请求 /111.php,而这个文件不存在,就不会启动 PHP application,不会执行 WordPress code,也不会产生 database query。这个 request 只是 miss,最后返回 404。

这改变了请求的 cost model。动态 application 中,一个 request 可能经过 routing、framework、authentication、rendering、database,甚至调用其他 service。我的静态路径 moving parts 少得多。当然,Nginx 仍然要接受 connection、处理 HTTP、查找 resource、写 log 并发送 response,但 application-level work 很少。

站点也用了 aggressive caching,它对正常静态内容交付很有帮助。不过我不会把所有随机 404 的低成本都归功于 cache。一个从未见过的无效 URI,在返回 404 前仍可能经历正常的 Nginx processing 和 filesystem lookup。

最重要的修正:静态不等于“天生安全”

这是我现在最想改写的一点。静态 architecture 能从 public request path 中移除很多 server-side component,但不会让安全工作消失。

  • 暴露的文件依然是暴露的。 如果 .git.env、backup 或 secret 被误放进 public directory,而 configuration 没有阻止,Nginx 可以像提供 HTML 一样高效地把它提供出去。
  • Nginx、TLS、操作系统和网络仍然属于 attack surface。 它们依然需要 patch 和正确配置。
  • Client-side code 仍然可能有 vulnerability。 静态 HTML 不会自动让 JavaScript、浏览器侧 auth logic 或 third-party script 变安全。
  • Resource exhaustion 依然可能发生。 connection、bandwidth、TLS handshake、worker capacity、log I/O 和 disk space 都是有限的。足够大的流量同样可以压垮静态站点。

所以更准确的结论不是“这些请求完全没关系”,而是:在这种架构里,很多针对特定应用的 probe 根本找不到可以执行的目标。

“每天数千次扫描”听起来可能比实际负载更可怕

日志会把 Internet background noise 放大,因为每一次失败 probe 都会单独出现一行。但每日总数本身不能说明 operational impact。几千个 request 均匀分布在 24 小时,与同样数量集中在短时间 burst 中,是完全不同的两种情况。

CPU 也只是一个 signal。我的 CPU 保持稳定,这是有价值的 evidence,说明我观察时这些流量没有造成明显 compute pressure。但它不能证明其他 resource 的成本也是 0。要认真评估影响,我还会看 request rate、bandwidth、active connections、Nginx worker utilization、response latency、log volume 和 disk usage。

我从这些噪音里学到了什么

对我来说最大的收获是:架构本身可以让整类自动化攻击变得基本无效,而不需要针对每一种 scanner signature 都写一条规则。bot 可以整天寻找 WordPress 和 PHP backdoor;如果 public request path 里既没有 WordPress 也没有 PHP,那么这类 probe 能利用的东西就很少。

但我现在不会再把“和我的 stack 无关”与“无害 traffic”混为一谈。我的当前 checklist 很简单:

  • 明确 public request path 真正能够到达哪些 runtime 和 service。
  • 把 secret、repository metadata、backup 和 build artifact 放在 web root 之外,或者明确 block。
  • 如果没有其他 targeting 或 impact 的 evidence,把反复出现的 404 probe 当作正常 Internet background noise。
  • 不要只看 CPU 就认定 abusive traffic 没有成本。
  • 即使 application 是静态的,也持续 patch Nginx、OS、TLS library 和 dependencies。

这段经历不能证明什么

我没有做 controlled benchmark,也没有模拟 DDoS。我只是观察 production logs 和 server load,同时每天有数千个 scanner request 到来。这足以让我对自己的 setup 得出实际结论,但不足以证明“静态站点永远获胜”这样的普遍规则。

我能更有把握确认的是一个更具体的事实:在由 Nginx 提供的 Next.js static export 上,我看到的大部分 scanner traffic 最终只是低成本 file miss 和 404,而没有进入 PHP runtime 或 database-backed application。观察期间 CPU 负载没有变化。

这才是我真正看重的 resilience 优势。更少的 moving parts 并不会让系统 invulnerable,只是让随机 Internet noise 更少有机会转化成真正的 application work。