编程

理解 Nginx 的 try_files

1651 2023-11-24 03:40:00

Nginx 的 try_files 指令实在有趣!它比乍看上去更有深度。

首先,Nginx 几乎可以不需要 try_files。没有它,Nginx 为静态文件提供服务也没问题:

server {
    listen 80;
    server_name _;

    root /var/www/html/public;
    index index.html index.html;
}

如果要支持 PHP,可以这样:

server {
    listen 80;
    server_name _;

    root /var/www/html/public;
    index index.html index.html;

    location ~ \.php$ {
        # pass off to PHP-FPM via fastcgi
    }
}

这实际上适用于静态文件和PHP应用的主页。一旦我们将路径引入到 URI(比如 example.com/foo/bar),它会崩溃。这便是使用 try_files 的时候。

添加 try_files

try_files 指令将会贯穿每个给定的选项,以尝试使用指令查找一个文件是否存在于服务器中。

server {
    listen 80;
    server_name _;

    root /var/www/html/public;
    index index.html index.html index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
}

对于给定的 URI,这段代码:

$uri 选项让 try_files 将该 URI 作为相对于根目录 root 的磁盘文件查找,本例为 /var/www/html/public

1️⃣ /css/app.css URI 将会搜索 /var/www/html/public/css/app.css

2️⃣ URI /foo/bar  将有 2 种行为 - 一个用于目录存在,一个用作不存在 

首先,$uri/ 选项让 try_files 将该 URI 当作目录并确认目录是否存在。如果 URI 关联到一个现有目录, Nginx 需要计算为该目录的哪个文件提供服务。

这便是 index 指令起作用的地方。由于 Nginx 只知道目录存在,我们需要通过 index 告诉 Nginx 哪些文件提供服务(如果文件存在)。你可以使用任何文件。第一个匹配的文件“胜出”。

3️⃣ 如果给定的 URI 匹配既不存在文件也不存在目录,那么 try_files 转到回退 URI - /index.php?$query_string;

其他 location 区块呢?

这个 location / 区块,以及其中的 try_files 指令,事实上与其他的 location 块一起生效!以下是稍微更完整的配置文件:

server {
    listen 80;

    server_name _;
    root /var/www/html/foobar.com;

    index index.html index.htm index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        # pass off to PHP-FPM via fastcgi
    }

    location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico)$ {
        expires    7d;
        access_log off;
        log_not_found off;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

如果该 try_files 指令解析/找到静态资源(css, js, 图片) 文件,那么第三个 locaction 块将实际处理该请求。这意味着 location / {}location ~* \.(<stuff>)$ {} 块都与这一请求相关!

当使用 PHP 文件时,情况相同 - 使用 location ~ \.php$ {} 块:

  1. index 解析到目录的 index.php 
  2. 当使用回退 /index.php?$requests_uri 
  3. 当 URI 给定的 PHP 文件存在时

在这些情况下,try_files 找到由 location ~ \.php$ {}  块“匹配的” PHP 文件,这将请求传递给 PHP-FPM。这是为什么 404 错误(无论是静态文件还是应用路由不存在)通常从 PHP 应用种返回。所有“不能在磁盘种找到文件”的真实用例传递给 /index.php 并因此经由该配置的 FastCGI 代理将请求发送给 PHP 应用。