前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >nginx 配置文件的匹配规则

nginx 配置文件的匹配规则

作者头像
烟草的香味
发布2021-07-23 12:40:48
1.8K0
发布2021-07-23 12:40:48
举报
文章被收录于专栏:烟草的香味

引出

之前在对php-fpm 进行nginx代理时, 为了对后台限定 IP 访问, 添加了如下配置:

代码语言:javascript
复制
location ^~ /admin {
    allow 127.0.0.1;
    deny all;
}

结果呢? 所有admin路径下的php文件, 全都没有解析, 变成文件下载了. 当时我不知道是什么问题, 不过将这段配置去掉之后, 问题就消失了. 所以, 我可以肯定的是, 一定是这段路径匹配的问题, 导致没有走php-fpm的解析.

探究

为了探究原因, 我查找资料并做了尝试. 如果想直接看结果, 可以跳过这一 part.

在上方出现问题的场景中, nginx的配置文件大体如下:

代码语言:javascript
复制
server {
    listen      80;
    server_name localhost;

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

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

    location ~ \.php$ {
        try_files                 $uri =404;
        //...此处省略 fpm 配置
    }

    location ^~ /admin {
        allow 127.0.0.1;
      	deny all;
    }
}

经过思考, 当我访问localhost/admin/test.php的时候, nginx没有执行第二个匹配规则, 没有将文件交由php-fpm解析器执行, 进而导致其作为静态文件直接下载.

接下来, 就是验证这个想法了. 最简单的验证方法, 就是在nginx匹配规则中, 直接返回 HTTP 响应吗. 这样用curl看一下响应码, 就知道执行了哪个规则了.

说干就干, 修改配置文件如下:

代码语言:javascript
复制
server {
    listen      80;
    server_name localhost;

    location / {
        return 300;
    }

    location ~ \.php$ {
        return 200;
    }

    location ^~ /admin {
        return 100;
    }
}

和猜想的一样, 即使匹配规则在前面, 但是仍然先匹配到了规则^~. 也就是说规则 ^~ 比规则 ~的匹配优先级更高.

不过还有一点无法确定, 即使先匹配到了后面的规则, 那也不能说明前面的规则就不走了啊. nginx也有可能是按照顺序依次进行匹配的.

为了验证, 我们将第三个配置规则中的return 100删掉. 此时, 如果能够匹配到php的规则, 那么就会返回响应码200, 如果不能, 应该提示找不到文件. 测试一下.

至此说明匹配到 ^~ 规则的时候, 就会直接执行而不进行后续的匹配了. 那问了, 有可能是因为两个匹配规则的优先级不同, 故而忽略了优先级低的匹配规则.

为了验证nginx对于相同优先级的匹配规则, 是否会进行后续匹配, 再次进行实验. 修改配置文件如下:

代码语言:javascript
复制
server {
    listen      80;
    server_name localhost;

    location ~ hp$ {
      return 400;
    }

    location ~ php$ {
      return 500;
    }
}

配置文件中两个正则匹配, 我的想法是这样的, 此时访问, 会返回响应吗 400, 说明匹配了第一个规则, 然后我将第一个规则中的return 400删除, 如果返回了 500, 就说明nginx在匹配了第一个规则之后, 继续执行了下一个匹配. 很严谨. 先访问一下:

很好, 符合预期, 然后将第一个规则中的return删除, 再次访问:

这次返回了 404, 这说明, nginx在执行到第一个匹配的时候, 就停止匹配, 不再进行后续匹配了.

至此, nginx的匹配规则基本上已经复现出来了.

  1. 按照优先级从高到低的顺序进行匹配
  2. 相同优先级的, 按照配置文件中的顺序进行匹配
  3. 当匹配到一条规则之后, 停止后续匹配.

匹配规则

接下来整理一下nginx路径的匹配规则, 以下优先级按照从高到底排序:

  • location = /xxx: 路径精确匹配
  • location ^~ /xxx: 路径前缀匹配
  • location ~ xxx: 路径正则匹配
  • location ~* xxx: 路径正则匹配, 不区分大小写, 与正则匹配的优先级相同
  • location /xxx : 路径前缀匹配
  • location / : 通用匹配, 当其他都没有匹配的时候, 会走到这里.

nginx会按照优先级从高到低依次进行匹配, 在第一个匹配成功的时候执行操作并停止匹配.

回顾

匹配规则看上去很简洁. 现在可以回头看一下我们最初遇到的问题了.

我们想让某后台地址限定 IP 访问, 故而添加了这样的配置:

代码语言:javascript
复制
location ~ \.php${
  //...
}
location ^~ /admin {
    allow 127.0.0.1;
    deny all;
}

现在应该很清楚了吧, 所有admin下的路径, 因为规则^~的优先级更高, 故而解析到了后面的规则, 而没有执行php的解析操作. 又因为没有解析操作, 故而 php 文件都当做资源文件返回了.

那么问题来了, 如果我想对admin路径下的路径执行访问限制, 改怎么办呢?

  • 将规则^~改成~ ? 不行, 因为优先级相同, 先匹配到前面的 php 正则匹配, 后面的限制没有效果
  • 将规则^~改成~并提到前面? 不行, 因为优先级相同, 先匹配到限制, 如果通过不会进行后面的 php 解析.

这不陷入死循环了么? 我又想对某个路径执行限制, 如果限制通过的话, 又需要能够正常解析. 怎么破? 这里我探索出来的思路是, 他不是不认识php文件么, 我让他认识认识不就完了么. 直接将匹配的解析过程嵌套写入, 配置文件大体如下:

代码语言:javascript
复制
    location ^~ /admin{
      deny all;
      location ~ \.php$ {
        //...
      }
    }

这样的话, 就可以达到在执行 IP 限制的前提下, 又能够正常解析php-fpm.

那么一个新的问题来了, 这不就相当于将 php 的解析复制了一遍么? 也太不优雅了. 我想到的方案是, 通过nginxinclude命令. 通过将php文件的解析配置单独放到一个配置文件php-fpm.conf.common文件中, 内容如下:

代码语言:javascript
复制
location ~ \.php${
  // ...
}

这样, 原本的配置文件就可以改写成如下形式了:

代码语言:javascript
复制
location ^~ /admin {
	allow 127.0.0.1;
	deny all;
	# 这里因为相对路径使用的是 nginx.conf 的路径, 所以需要再走一层
	include ./conf.d/php-fpm.conf.comon
}
include ./conf.d/php-fpm.conf.common

此时, 就能够实现之前的目的了, admin路径下的php文件仅对指定 ip 开放, 且通过时能够正常进行解析.

有可能有更优雅的解决方案, 我看网上有些实现是通过rewrite的方式来实现的, 但是我试了很多次都没有成功. 如果你有更好的方式, 还望不吝赐教.


经过几天的实验, 终于把nginx的执行顺序搞懂了, 感谢我的中学老师教会了我控制变量法.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 烟草的香味 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引出
  • 探究
  • 匹配规则
  • 回顾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档