Nginx location 块匹配规则与实例详解


哈喽,各位小伙伴!今天我们来聊一个Nginx中非常核心且关键的配置——location块。无论你是刚入门的新手,还是已经有一定经验的老手,彻底搞懂location的匹配规则,对于写出高效、准确的Nginx配置都至关重要。

本文将用最口语化的方式,带你深入理解location的各种匹配规则,并结合代码和实际案例,让你彻底告别Nginx配置的“玄学”。

location 是个啥?

简单来说,Nginx中的location指令就是用来给不同的URL请求“指路”的。它存在于server配置块中,通过定义不同的URI匹配模式,告诉Nginx收到符合条件的请求后应该如何处理。

server {
    listen 80;
    server_name example.com;

    location / {
        # 通用处理逻辑
    }

    location /images/ {
        # 处理图片请求的逻辑
    }

    location /api/ {
        # 处理API请求的逻辑
    }
}

上面这个例子就是一个最基本的server配置,里面包含了三个location块,分别用来处理根目录、图片目录和API相关的请求。

location 的匹配规则

location的语法看起来很简单,但其匹配规则却有优先级之分,这也是很多人容易混淆的地方。 它的基本语法如下:

location [ 修饰符 ] uri { ... }

关键就在于这个可选的“修饰符”,它决定了location的匹配方式。 我们一个个来看:

修饰符 含义
(无) 前缀匹配,匹配以指定uri开头的请求。
= 精确匹配,请求的URI必须和指定的uri完全相同。
^~ “非正则”前缀匹配,匹配以指定uri开头的请求,一旦匹配成功,就不再进行后续的正则匹配。
~ 正则匹配,区分大小写。
~* 正则匹配,不区分大小写。

匹配顺序大揭秘

Nginx在处理一个请求时,会按照一套明确的规则来寻找最佳的location块。 这个过程可以总结为以下几步:

  1. 精确匹配 (=) 优先:Nginx会最先检查所有使用=修饰符的location。如果请求的URI与某个精确匹配的location完全一致,Nginx会立即选择这个location来处理请求,不再进行任何后续的搜索。

  2. 前缀匹配:如果第一步没有找到精确匹配,Nginx会开始查找所有前缀匹配的location(包括带^~修饰符和不带修饰符的)。Nginx会找到并记住最长的那个匹配项。

  3. **特殊前缀匹配 (^~)**:如果在上一步找到的最长前缀匹配恰好使用了^~修饰符,那么Nginx会直接采用这个location,并且不再进行后续的正则匹配检查。

  4. 正则匹配 (~~*)**:如果最长前缀匹配没有使用^~,Nginx会继续按照配置文件中的书写顺序**,从上到下检查所有的正则匹配location。一旦找到第一个匹配的正则表达式,就会立即停止搜索,并使用这个location来处理请求。

  5. 最终选择:如果在正则匹配阶段没有找到任何匹配项,那么Nginx就会回头使用第二步中记住的那个最长的前缀匹配location

一句话总结优先级
= > ^~ > 正则表达式(~~* > 普通前缀匹配

实例解析

光说不练假把式,我们来看一个综合性的例子,让你彻底明白这些规则是如何应用的。

假设我们有以下nginx.conf配置:

server {
    listen 80;
    server_name localhost;

    # 规则A:精确匹配
    location = / {
        return 200 "This is exact match for / [A]";
    }

    # 规则B:普通前缀匹配
    location / {
        return 200 "This is prefix match for / [B]";
    }

    # 规则C:普通前缀匹配
    location /user/ {
        return 200 "This is prefix match for /user/ [C]";
    }

    # 规则D:特殊前缀匹配
    location ^~ /images/ {
        return 200 "This is ^~ prefix match for /images/ [D]";
    }

    # 规则E:正则匹配(不区分大小写)
    location ~* \.(gif|jpg|jpeg)$ {
        return 200 "This is regex match for images [E]";
    }
}

现在,我们来分析几个不同的请求会匹配到哪个规则:

  • 请求 /

    • 分析:Nginx首先会寻找精确匹配。规则A (location = /) 完美命中。
    • 结果:返回 “This is exact match for / [A]”。
  • 请求 /index.html

    • 分析:没有精确匹配。接着进行前缀匹配,规则B (location /) 匹配,且是最长的前缀匹配。然后Nginx会继续查找正则匹配,但没有能匹配.html的正则规则。
    • 结果:使用之前记住的最长前缀匹配,返回 “This is prefix match for / [B]”。
  • 请求 /user/profile

    • 分析:没有精确匹配。进行前缀匹配,规则B (/) 和规则C (/user/) 都匹配,但/user/是更长的前缀匹配。没有匹配的正则规则。
    • 结果:使用最长的前缀匹配规则C,返回 “This is prefix match for /user/ [C]”。
  • 请求 /images/logo.jpg

    • 分析:没有精确匹配。进行前缀匹配,规则B (/) 和规则D (/images/) 都匹配,/images/是更长的前缀匹配。因为规则D使用了^~修饰符,所以Nginx会立即选择它,并且不会再检查后续的正则匹配规则E。
    • 结果:返回 “This is ^~ prefix match for /images/ [D]”。
  • 请求 /path/to/some/file.JPG

    • 分析:没有精确匹配。最长的前缀匹配是规则B (/)。然后按顺序检查正则匹配,规则E (~* \.(gif|jpg|jpeg)$) 因为不区分大小写,成功匹配了.JPG
    • 结果:使用正则匹配规则E,返回 “This is regex match for images [E]”。

实际应用案例

1. 动静分离

这是location最常见的应用场景之一。我们希望Nginx直接处理静态文件请求,而将动态请求(如PHP)转发给后端的应用服务器。

server {
    root /var/www/project;

    # 处理所有PHP请求
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # 处理静态文件请求
    location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 30d; # 设置浏览器缓存30天
    }

    # 其他所有请求都交给index.php处理(适用于很多框架)
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
}
  • 解析
    • 使用正则匹配将所有以.php结尾的请求交给FastCGI处理器。
    • 使用不区分大小写的正则匹配来捕获所有常见的静态文件请求,并为其设置一个较长的浏览器缓存时间。
    • 通用的/前缀匹配作为兜底,使用try_files指令,尝试查找请求的文件或目录,如果都找不到,则将请求重写到index.php,这对于像WordPress、Laravel这样的框架非常有用。

2. 反向代理与API网关

location也非常适合用来做反向代理,将不同路径的请求转发到不同的后端服务。

server {
    server_name api.example.com;

    # 用户服务的API
    location /user/ {
        proxy_pass http://user_service_backend;
    }

    # 订单服务的API
    location /order/ {
        proxy_pass http://order_service_backend;
    }

    # 兜底的根路径
    location / {
        return 404; # 未匹配的API路径返回404
    }
}

upstream user_service_backend {
    server 127.0.0.1:8001;
}

upstream order_service_backend {
    server 127.0.0.1:8002;
}
  • 解析
    • 通过前缀匹配,将所有/user/开头的请求代理到用户服务。
    • 将所有/order/开头的请求代理到订单服务。
    • 这样就实现了一个简单的API网关,将不同的微服务聚合在同一个域名下。

总结

掌握Nginx的location匹配规则是每个Web开发者和运维工程师的必备技能。记住那个优先级顺序,并通过实际的案例多加练习,你就能游刃有余地编写出清晰、高效的Nginx配置了。

希望这篇文章能帮你彻底理清思路,如果觉得有帮助,不妨点个赞分享一下吧!


  目录