深入探讨nginx,为啥不能使用双斜杠配置location

1,697 阅读7分钟

故事背景:一个线上运行很久的项目,现在开启了以前没有用的功能,但是这个没有用的功能,客户端有一个bug,将URI的内容多写了一个斜杠,导致404错误,因为一部分客户端有错误,一部分又是正确的,然后客户端又无法立马升级更新。

怎么办呢,我们首先想到的使用Nginx代理处理一下,本文带你一起来看看我们的处理中的踩坑之路。

注意:阅读本文将默认你已经掌握Nginx配置,基础的都可以上手搞定。

一、location 指令详解

location指令用于在配置文件中定义请求URL路径的匹配规则和处理逻辑。它的作用是根据请求的URL路径,确定要应用的配置块,并执行相应的处理操作。

它的基本语法如下:

location [匹配类型] 路径 {
    ...  // 配置指令
}
  • [匹配类型]:指定匹配的类型,可以是精确匹配、前缀匹配、正则表达式匹配或最长前缀匹配。

  • 路径:指定要匹配的URL路径。

1.1 location工作流程

  1. 当接收到一个请求时,Nginx会按照配置文件中的顺序逐个匹配location块。

  2. Nginx会比较请求的URL路径与每个location块中定义的路径匹配规则。

  3. 当找到与请求的URL路径匹配的location块时,Nginx会执行该location块中定义的配置指令。

  4. 如果没有找到匹配的location块,则会使用默认的location块,通常是最长前缀匹配的location块。

  5. 执行匹配到的location块中定义的配置指令,如代理转发、重定向、缓存控制、静态文件服务等。

  6. 返回处理结果给客户端。

graph TD;
    A[接收到请求] --> B{是否有匹配的location块?};
    B -- 是 --> C{是否有精确匹配的location块?};
    C -- 是 --> D[执行精确匹配的location块的配置指令] --> J;
    C -- 否 --> E{是否有前缀匹配的location块?};
    E -- 是 --> F[执行前缀匹配的location块的配置指令] --> J;
    E -- 否 --> G{是否有正则表达式匹配的location块?};
    G -- 是 --> H[执行正则表达式匹配的location块的配置指令] --> J;
    G -- 否 --> I[执行默认的最长前缀匹配的location块的配置指令];
    I --> J[执行匹配到的location块的配置指令];
    J --> K[返回处理结果给客户端];
    B -- 否 --> I;

style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px
style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px
style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px
style E fill:#98FB98,stroke:#98FB98,stroke-width:2px
style F fill:#B2FFFF,stroke:#B2FFFF,stroke-width:2px
style G fill:#ADD8E6,stroke:#ADD8E6,stroke-width:2px
style H fill:#E6E6FA,stroke:#E6E6FA,stroke-width:2px
style I fill:#EEDD82,stroke:#EEDD82,stroke-width:2px

1.2 location四种匹配场景

不同的location匹配类型适用于不同的使用场景;通过合理使用不同类型的location匹配,可以根据实际需求对特定的路径请求进行不同的处理,包括代理转发、缓存控制、重定向等操作。

下面我将详细说明四种不同的location匹配场景:

location = /about {
    # 处理精确匹配的/about请求
    ...
}
  • 精确匹配:使用=进行精确匹配,例如 location = /about。这种匹配适用于处理特定的路径请求,只匹配完全相等的路径。例如,当用户访问 /about 时,可以配置该location块来处理该请求,如返回关于页面的内容或执行特定的操作。

    • 优点:精确匹配非常准确,只处理与指定路径完全相等的请求,适用于需要精确处理特定路径的场景。

    • 缺点:需要明确指定每个精确匹配的路径,如果有大量精确匹配的路径,配置可能变得繁琐。

    • 适用场景:处理少量具有确定路径的请求,如特定页面或特定功能的入口。

location ^~ /static/ {
    # 处理以/static/开头的静态资源请求
    ...
}
  • 前缀匹配:使用^~进行前缀匹配,例如 location ^~ /static/。这种匹配适用于处理以指定前缀开头的路径请求。例如,当用户请求 /static/css/style.css 或 /static/js/script.js 时,可以配置该location块来处理静态资源的请求,如指定对应的文件路径或启用缓存机制。

    • 优点:前缀匹配可以处理以指定前缀开头的请求,适用于处理静态资源或特定路径前缀的请求。

    • 缺点:如果存在多个前缀匹配的路径,可能会产生匹配冲突,需要注意匹配顺序的影响。

    • 适用场景:处理静态资源、CDN加速、反向代理等场景,其中具有共同前缀的请求需要特定处理。

location ~ \.(jsp|html)$ {
    # 处理以.jsp或.html结尾的请求,使用正则表达式匹配
    ...
}
  • 正则表达式匹配:使用~或~*进行正则表达式匹配,例如 location ~ .(jpg|png)$。这种匹配适用于处理符合正则表达式模式的路径请求。例如,当用户请求以 .jpg 或 .png 结尾的图片文件时,可以配置该location块来处理这些图片文件的请求,如指定对应的图片存储路径或进行图片压缩处理。

    • 优点:正则表达式匹配非常灵活,可以通过复杂的模式匹配满足不同的请求需求。

    • 缺点:正则表达式匹配的性能相对较低,配置的模式复杂性可能导致匹配效率下降。

    • 适用场景:处理具有复杂路径结构、需要进行模式匹配的请求,如处理特定文件类型、特定路由规则等。

location / {
    # 处理根路径请求
    ...
}
  • 最长前缀匹配默认情况下,如果没有使用精确匹配、前缀匹配或正则表达式匹配的location块,Nginx会使用最长前缀匹配。例如 location /。这种匹配适用于处理所有未匹配的路径请求。例如,当用户请求任意路径时,可以配置该location块来处理这些未匹配的请求,如执行默认的处理逻辑或返回错误页面。

    • 优点:最长前缀匹配是默认的匹配方式,可以处理所有未匹配的请求,具有广泛的适用性。

    • 缺点:当存在多个location块时,最长前缀匹配可能导致某些路径被意外匹配并执行不正确的处理逻辑。

    • 适用场景:处理未匹配的请求、默认请求处理或错误处理等场景。

二、为啥不能使用双斜杠配置location

先说看看下面的配置(配置X):

image.png

上面这个配置是否正确呢?

先别着急回答,因为你现在直接回答正确或者错误都是不对的,我们一起向下看看。

2.1 认识一下merge_slashes

Nginx 配置文件中明确设置 merge_slashes 指令,那么 Nginx 将使用其默认行为。默认情况下,Nginx 会合并请求 URI 中的重复斜杠(// 变为 /),除非配置了其他行为。

所以我们下面将分两种情况来介绍一下merge_slashes的使用结果:

2.1.1 没有配置merge_slashes的时候

没有配置merge_slashes的时候,//是不行的(Nginx会合并斜杠);这个时候正确的写法如下

image.png

注意:斜杠的个数

2.1.2 配置merge_slashes的时候

  • 当我们配置了merge_slashes的时候,//是可以的(Nginx不会合并斜杠);这个时候正确的写法如下:

image.png

这里和‘配置X’是不是有点像呢? 注意:当配置了merge_slashes off; 之后,里面的正则匹配就可以使用//写法了

  • 这里使用单斜杠配置会怎么样呢(反例)?

image.png

可以明确的告诉大家,当这里使用单斜杠的时候,我们是进不去里面的location的。

2.1.3 找一下源码支持的部分

下面是nginx源码相关uri解析的代码截图。

image.png

2.2 特别注意

在 Nginx 的较新版本中,merge_slashes 指令已经被 merge_slashes off; 取代,后者用于明确禁止 Nginx 合并斜杠。如果这个指令没有出现在配置文件中,Nginx 将采用默认行为,即合并斜杠。如果你需要改变这个行为,你应该在配置文件中明确设置 merge_slashes off;

所以,在排查问题的时候,一定要看看具体的Nginx版本,最好就是看看有没有这个配置。

2.3 测试地址

注意:这里使用测试的uri,默认是带两个斜杠的,例如://test-demo/appRule/getInfo

三、总结

Nginx本身是非常强大的,它考虑了很多我们说没遇到的场景,这些场景的考虑可能个为你使用过程中增加了迷惑,但是只要我们沉下心来,仔细观察,总能找到原因。

希望本文对您有所帮助。如果有任何错误或建议,请随时指正和提出。

同时,如果您觉得这篇文章有价值,请考虑点赞和收藏。这将激励我进一步改进和创作更多有用的内容。

感谢您的支持和理解!