bigywater.tech
常见报错 · 38

Nginx 404 与 403:root 和 alias 的路径拼接、目录请求为什么是 403、SPA 的 try_files 让缺失资源返回 200

“404 Not Found” 和 “403 Forbidden” 在 nginx 里往往是路径拼错或没有 index 文件。这篇在本机起自己的 nginx,把 root 与 alias 的路径拼接规则、结尾斜杠的陷阱、目录请求为什么是 403、权限怎么引起 403,以及 SPA 回退的坑逐个测了,每个都对照 error.log 里 nginx 实际去找的路径。

filesa.txt
alias 缺结尾斜杠时,请求 /static/a.txt 被拼成 data/filesa.txt
403
目录没有 index 文件时 nginx 返回的状态码(不是 404),日志是 directory index … is forbidden
200
SPA 回退配置下,不存在的 /assets/missing.js 返回的状态码(返回的是 index.html)

先看 error.log

实测 · 本机执行输出
  GET /static/b.txt            -> 200 'static/b.txt'                    
  GET /static/nope.txt         -> 404 '404 Not Found'                    error.log: open() "<tmp>/data/static/nope.txt" failed (2: No such file or directory), client: 127.0.0.1, server: , request: "GET /static/nope
  GET /files/a.txt             -> 200 'files/a.txt'                     
  —— 常见的错:想把 /static/ 指到 data/files,写成 root data/files:
  GET /static/a.txt            -> 404 '404 Not Found'                    error.log: open() "<tmp>/data/files/static/a.txt" failed (2: No such file or directory), client: 127.0.0.1, server: , request: "GET /static/a

404 时 error.log 写着 nginx 实际找的路径,对照期望的路径就知道哪里拼错。

root 与 alias

实测 · 本机执行输出
  location /static/ + alias .../files/(两边都有斜杠):
  GET /static/a.txt            -> 200 'files/a.txt'                     
  location /static/ + alias .../files(alias 缺斜杠):
  GET /static/a.txt            -> 404 '404 Not Found'                    error.log: open() "<tmp>/data/filesa.txt" failed (2: No such file or directory), client: 127.0.0.1, server: , request: "GET /static/a.txt HTT
  location /static + alias .../files/(location 缺斜杠):
  GET /static/a.txt            -> 200 'files/a.txt'                     
  GET /staticx.txt             -> 404 '404 Not Found'                    error.log: open() "<tmp>/data/files/x.txt" failed (2: No such file or directory), client: 127.0.0.1, server: , request: "GET /staticx.txt HTT
  —— 正则 location 里的 alias 要用捕获组:
  GET /dl/a.txt                -> 200 'files/a.txt'                     
  GET /dl/nope.txt             -> 404 '404 Not Found'                    error.log: open() "<tmp>/data/files/nope.txt" failed (2: No such file or directory), client: 127.0.0.1, server: , request: "GET /dl/nope.txt 

root 接整个 URI,alias 替换匹配部分;结尾斜杠要两边一致。

目录请求

实测 · 本机执行输出
  GET /dir/                    -> 403 '403 Forbidden'                    error.log: directory index of "<tmp>/data/dir/" is forbidden, client: 127.0.0.1, server: , request: "GET /dir/ HTTP/1.1", host: "127.0.0.1:97
  GET /dir                     -> 301 '301 Moved Permanently'             Location: http://127.0.0.1:9780/dir/
  autoindex on:GET /dir/ -> 200,页面里有 "note.txt":True

没有 index 文件是 403;没有尾斜杠会 301;autoindex 才显示列表。

权限

实测 · 本机执行输出
  GET /files/a.txt             -> 403 '403 Forbidden'                    error.log: open() "<tmp>/data/files/a.txt" failed (13: Permission denied), client: 127.0.0.1, server: , request: "GET /files/a.txt HTTP/1.1",
  GET /locked/secret.txt       -> 403 '403 Forbidden'                    error.log: open() "<tmp>/data/locked/secret.txt" failed (13: Permission denied), client: 127.0.0.1, server: , request: "GET /locked/secret.tx
  GET /locked/secret.txt       -> 200 'secret'                          

worker 用户要有路径上每级目录的 x 权限和文件的 r 权限。

SPA 回退

实测 · 本机执行输出
  GET /app/deep/route          -> 200 '<html>SPA index</html>'          
  GET /assets/app.js           -> 200 'console.log(1)'                  
  GET /assets/missing.js       -> 200 '<html>SPA index</html>'          
  GET /app/deep/route          -> 200 '<html>SPA index</html>'          
  GET /assets/app.js           -> 200 'console.log(1)'                  
  GET /assets/missing.js       -> 404 '404 Not Found'                   

try_files 回退到 index.html 会让缺失的静态资源变成 200 的页面;静态资源目录单独加 =404。

分步演示

路径拼接
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64;报错文本都是真实复现时原样捕获的(命令行工具统一用 LC_ALL=C 取英文原文,路径换成 );所有服务都是本机上自己起的、只监听 127.0.0.1,不访问外网;计时用 time.perf_counter,数字每次运行会有小幅抖动。本篇用 nginx/1.31.7;每个实验起一个自己的 nginx(自己的前缀目录和配置,只监听 127.0.0.1:9780),网站文件在实验目录的 data/ 下;worker 进程以我自己的账号运行,所以权限实验用 chmod 构造
    • 没有验证:root 运行 nginx 并切换到 www-data 等用户之后的权限问题;SELinux / AppArmor 引起的 403(Linux 专有);符号链接与 disable_symlinks;index 指令多个文件的回退顺序;try_files 与 error_page 的组合;大小写敏感(macOS 默认不区分大小写的文件系统与 Linux 不同);location 匹配优先级(前缀、正则、^~、=)
    京ICP备2026067502号-1京公网安备11010502063487号