静态托管与 PHP FastCGI

静态托管与 PHP FastCGI

用墨守替代 Nginx / Caddy:静态目录托管(SPA 回退、预压缩、点文件拒绝)与 php-fpm 回源(WordPress / ThinkPHP)。

墨守不只是挡在 Nginx 前面——它可以直接替掉 Nginx / Caddy。站点的回源类型有三种,互斥三选一:反向代理(upstream)、静态目录托管(static)、PHP FastCGI(fastcgi)。简单部署场景下,一个静态站或一个 WordPress 站不再需要另装 Web 服务器。

#静态目录托管

在「站点管理 → 新增站点」把回源类型选「静态」,填根目录即可。HTTP 语义走标准实现:ETag / 304、Range 断点续传、If-Modified-Since、sendfile 一应俱全。

字段 / 控件默认 / 示例说明
root(必填)静态根目录,如 /var/www/example
index["index.html"]目录索引文件。
spa_fallback(关)无扩展名且未命中的路径回退到该目标(如 /index.html)。带扩展名的资源缺失仍返回 404,不会把 JS 请求回退成 HTML。
precompressedtrue优先返回同名的 .br / .zst / .gz 预压缩文件,按 Accept-Encoding 协商。
compress_on_the_flyfalse没有预压缩文件时,对可压缩类型实时 gzip(brotli 请用预压缩文件)。
cache_control[]按 glob 设 Cache-Control,如给 /assets/* 设长缓存(仅配置文件)。
error_pages{}自定义错误页,如 {"404": "/404.html"}(root 内,仅配置文件)。
hidden_denytrue默认拒绝点文件(.git / .env / .htaccess / .user.ini…),except 可放行如 .well-known
follow_symlinksfalse默认不跟随跳出 root 的符号链接。
dir_listfalse目录列表。
静态站点配置
sites:
  - host: "www.example.com"
    static:
      root: "/var/www/example"
      spa_fallback: "/index.html"    # SPA 应用
      precompressed: true
命中真实静态文件的 GET / HEAD 走快速路径:就地发文件,跳过语义检测与智能缓存。过载保护、访问控制、限流、Bot 防护、等候室这些外层照常先跑。受 认证网关 管辖的站点不走快速路径,否则静态文件会绕过登录被直接读走。

#路径安全

静态服务最经典的漏洞是路径穿越。墨守的解析顺序是固定的:

  1. 1
    NUL 与非法 UTF-8 → 400先挡掉畸形字节。
  2. 2
    解编码绊线 → 400与检测器同款的归一化解码,%2e%2e、双重编码、overlong、反斜杠等 ../ 变体一律拒绝。
  3. 3
    结构性容纳path.Clean 根锚定 + join + 相对路径复检,即使绊线没响也逃不出 root。
  4. 4
    点文件与符号链接点文件拒绝;符号链接校验最终目标仍在 root 内。
响应始终带 X-Content-Type-Options: nosniff,Content-Type 按扩展名给出、不做内容嗅探——上传的 .jpg 即使内含 PHP 也只会当图片发。

#PHP FastCGI(替代 Nginx + php-fpm)

回源类型选「PHP」,填 php-fpm 地址与站点根目录。行为对齐 Caddy 的 php_fastcgi:真实静态文件本地直发,.php 与前端控制器交给 php-fpm。

WordPress 站点
sites:
  - host: "blog.example.com"
    fastcgi:
      address: "unix:/run/php/php-fpm.sock"   # 或 tcp:127.0.0.1:9000
      root: "/var/www/wordpress"
      index: "index.php"
ThinkPHP / 通用前端控制器
sites:
  - host: "app.example.com"
    fastcgi:
      address: "tcp:127.0.0.1:9000"
      root: "/var/www/app/public"             # ThinkPHP 指向 public 目录
      index: "index.php"

控制台里更省事:站点管理 → 新增站点 → 回源类型选「PHP」→ 点「WordPress」或「ThinkPHP」一键模板 → 核对根目录与 php-fpm 地址 → 保存,即时生效。

try_files 决策顺序

  1. 1
    安全清理路径穿越直接 404。
  2. 2
    分割脚本与 PATH_INFO/x.php/y → 脚本 /x.php + PATH_INFO /y
  3. 3
    脚本真实存在 → 交 php-fpm这一步是安全不变式的关键。
  4. 4
    是真实的非脚本文件 → 本地静态发送图片、CSS、JS 走静态路径。
  5. 5
    是目录且有 index.php → 交 php-fpm
  6. 6
    前端控制器回退 → root 的 index.php伪静态永久链接走这条;REQUEST_URI 原样传给 PHP,由 WordPress / ThinkPHP 自行路由。
  7. 7
    都不匹配 → 404
PATH_INFO 漏洞已被根除安全不变式:交给 FastCGI 的 SCRIPT_FILENAME 永远是一个真实存在的 .php 文件。伪造 PATH_INFO 后缀(/uploads/evil.jpg/x.php,而 x.php 并不存在)不会让非脚本文件被执行——经典的 cgi.fix_pathinfo / Nginx .jpg/.php 漏洞在代理层就被根除。root 之外的 SCRIPT_FILENAME 一律拒绝。
动态请求(走 FastCGI 的)必走完整八层检测链路,绝不走静态快速路径。只有真实静态文件命中才会 fast-path。
权限提示:php-fpm 需要能读 root 目录;若用 unix socket,墨守进程需对该 socket 有读写权限(同组或 0666)。systemd 沙箱下把 socket 目录加进 ReadWritePaths

#明确不支持 .htaccess

墨守不读取、不执行 .htaccess / .user.ini(且默认拒绝它们对外响应)。Apache 伪静态请用 CMS 后台生成的 Nginx 版规则,按 伪静态 URL 重写 转换成墨守格式。

部署完静态站或 PHP 站,强烈建议再开一道 静态内容防篡改——对 PHP 站尤其能挡 WebShell。