Nginx配置文件详解

1、location 的匹配规则

location 的语法规则:

location [=|~|~*|^~|@] /uri/ {
  ...
} 
  • = :表示精确匹配后面的url
  • ~ :表示正则匹配,但是区分大小写(注意~ ^ 和^~的区别 ,~ ^ /xxx/ 意思是匹配以 /xxx/ 开头的资源)
  • ~* :正则匹配,不区分大小写
  • ^~ :表示普通字符匹配,如果该选项匹配,只匹配该选项,不匹配别的选项,一般用来匹配目录
  • @ :"@" 定义一个命名的 location,使用在内部定向时,例如 error_page

 

当有多条 location 规则时,nginx 有一套比较复杂的规则,优先级如下:

  1. 精确匹配 =
  2. ^~ 前缀匹配 (立刻停止后续的正则搜索)
  3. 按文件中顺序的正则匹配 ~或~*
  4. 匹配不带任何修饰的前缀匹配。

这个规则大体的思路是:先精确匹配,没有则查找带有 ^~的前缀匹配,没有则进行正则匹配,最后才返回前缀匹配的结果(如果有的话)。

 

示例:

location = / {
   #规则A
}
location = /login {
   #规则B
}
location ^~ /static/ {
   #规则C
}
location ~ \.(gif|jpg|png|js|css)$ {
   #规则D
}
location ~* \.png$ {
   #规则E
}
location / {
   #规则F
}

效果:

访问根目录 /, 比如 http://localhost/ 将匹配规则 A
访问 http://localhost/login 将匹配规则 B,http://localhost/register 则匹配规则 F
访问 http://localhost/static/a.html 将匹配规则 C
访问 http://localhost/a.gif, http://localhost/b.jpg 将匹配规则 D和规则 E,但是规则 D 顺序优先,规则 E不起作用,而 http://localhost/static/c.png则优先匹配到规则 C
访问 http://localhost/a.PNG 则匹配规则 E,而不会匹配规则 D,因为规则 E 不区分大小写
访问 http://localhost/category/id/1111 则最终匹配到规则 F,因为以上规则都不匹配。这个时候一般可以配置 nginx 转发请求给后端应用服务器,比如 FastCGI(PHP),tomcat(jsp),nginx 作为反向代理服务器存在

 

1.1、实际使用示例

在实际使用中,一般会至少配置三个匹配规则,如下:

# 直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。
# 这里匹配我们的静态首页,也可以转发给后端应用服务器
# 第一个必选规则
location = / {
    root    /usr/appname;
    index    index.html;   
    #proxy_pass http://tomcat:8080/index
}

# 第二个必选规则是处理静态文件请求,这是 nginx 作为 http 服务器的强项
# 有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用
location ~* \.(html|gif|jpg|jpeg|png|css|js|ico)$ {
    root  /usr/appname;
    expires 1m;
}
location ^~ /static/ {
    root /webroot/static/;
}


# 第三个规则就是通用规则,用来转发动态请求到后端应用服务器
# 非静态文件请求就默认是动态请求,自己根据实际把握
location / {
    proxy_pass http://tomcat:8080/
}

 

一个推荐可供参考的配置文件:

    upstream myserver {
        hash $http_x_forwarded_for;
        #hash $remote_addr;
        #sticky;
        #server 192.168.118.128:9080;
        server 192.168.118.129:9080;
        check_interval=3000 rise=2 fall=5 timeout=1000 type=http;
        check_http_send "GET / HTTP/1.1\r\nConnection:keep-alive\r\n\r\n";
        check_http_expect_alive http_2xx http_3xx http_4xx;
        keepalive 100;
    }

    server {
        listen 8090;
        sever_name localhost;

        if($request_method !~ ^(GET|POST)) {
            return 444;
        }

        location =/ {
            root /root/myweb;
            index login/index.html;
        }

        location ~* \.(html|gif|jpg|jpeg|css|js|png|ico|eot|ttf|woff|svg)$ {
            root /root/myweb;
            expires 1m;
        }

        location / {
            proxy_pass http://myserver;
            real_ip_header X-http_x_forwarded_for;
            proxy_set_header Connection "";
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

 

1.2、精准匹配出现的问题

当配置的 location 的 URI 是目录而不是资源文件时,并且命中该 location 最后实际会访问资源文件,此时 Nginx 会再次接收到一个获取资源文件的请求,Nginx 会再次根据资源文件的请求的路径来匹配 location 并作出相应。

比如:

server {
    listen       80;
    server_name  localhost;

    location = /abc/ {
        root   /usr/webProjects/webFirst;
        index  abc.html;
    }
}

假设 Nginx 服务器上的 ip 为 192.168.118.128,我们通过浏览器请求 http://192.168.118.128/abc/,此时首先会命中 “location = /abc/” 规则,但你会发现浏览器可能会报 404,查看 Nginx 日志可以看到实际上并没有访问到 /usr/webProjects/webFirst/abc/abc.html 资源,而实际上是访问了 "/usr/local/nginx/html/abc/abc.html"  下的资源。报错日志如下:

 这是因为上面的配置中当命中 /abc/ 路径时,最后会访问到 abc.html ,Nginx 会再次接收到一个访问 /abc/abc.html 的请求,此时会再次根据 /abc/abc.html 来进行匹配。但是上面我们并没有配置该路径,所以最后会匹配到一个默认配置,即路径是 /,root 是 Nginx 安装目录下的 /usr/local/nginx/html 目录,所以最后访问了 "/usr/local/nginx/html/abc/abc.html"  下的资源而导致报错。

 

所以我们最好要加上一个匹配 /abc/abc.html 的配置,以便最后能访问到我们希望访问的资源:

server {
    listen       80;
    server_name  localhost;

    location = /abc/ {
        root   /usr/webProjects/webFirst;
        index  abc.html;
    }

    location = /abc/abc.html {   #上面的配置最后会命中到这里
        alias   /usr/webProjects/webFirst/user/user.html;
    }

    location = /abc/index.html {  #当上面的location=/abc/没有配置index时,最后会匹配到这里
        alias   /usr/webProjects/webFirst/user/user.html;
    }

    location ~* \.(html|gif|jpg|jpeg|png|css|js|ico)$ {  #这里取代Nginx中的默认配置,即location路径为 /,root 为Nginx安装目录下的html文件夹
        root   /usr/webProjects/web01;
        expires 1m;
    }
}

 

1.3、正则匹配

Nginx 常见正则符合:

  • ^:匹配字符串的开始位置
  • $:匹配字符串的结束位置
  • .*: .匹配任意字符,*匹配数量0到正无穷
  • \. 斜杠用来转义,\. 表示匹配点符号 "."
  • (值1|值2|值3|值4):表示或匹配。例:(jpg|gif|png|bmp)匹配jpg或gif或png或bmp

 

2、root(服务器资源路径)

Nginx 中的 root 可以在 location 或者直接在 server 中配置,root 指定的是资源存放在服务器中的路径。root 既可以是绝对路径,也可以是相对路径,以 / 开头即为绝对路径。比如 location 中的 root 指定的是这个 location 规则所匹配的资源所存放的资源路径。

相对路径使用示例:

server {
        listen       80;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }
}

上面 location 中 root 所指向的 html 就是一个相对路径,相对的是当前这个配置文件的路径。假设此配置文件的位置是 /etc/nginx/conf.d,那么这个 html 的绝对路径就是 /etc/nginx/conf.d/html/。因此为避免出现不必要的麻烦,在配置 root 路径的过程中最好用绝对路径。

绝对路径使用示例:

server {
        listen       80;
        server_name  localhost;

        location / {
            root   /usr/local/nginx/html;
            index  index.html index.htm;
        }

        location /webTestProject/ {
            root   /usr/myTestData/;
            index  index.html index.htm;
        }
}

请注意,上面当我们访问 http://ip/webTestProject 时,实际上是访问了服务器中的 /usr/myTestData/webTestProject 路径。也就是说,当配置 root 时,最后访问到的资源路径会加上 location 中匹配到的 url。

 

Nginx中, 如果浏览器访问的 URI 最后带斜杠,比如:http://localhost/product/ ,则默认查找 product下的index页面,存在就返回;不存在且未开启自动索引目录选项(指令是:autoindex on),则报403错。如果浏览器访问的 URI 最后不带斜杠,比如:http://localhost/product,则会查找 product 文件,存在就返回。否则会自动将 uri 补全为 http://localhost/product/ ,浏览器自动发生 301 重定向跳转。

 

2.1、server中的root和location的root的区别

如果我们同时在 server 和 location 中都设置了 root 路径,例如:

server {
        listen       80;
        server_name  localhost;

        root   /usr/local/nginx2/html;

        location / {
            root   /usr/local/nginx/html;
            index  index.html index.htm;
        }
}

当我们访问服务器命中了 location 的配置时,nginx 的 location 会优先匹配到此代码块,会指向 location 中所配置的 root , server 中的 root 不会生效。当 nginx 找不到匹配到的 location 或者 location 中没有配置 root 时,此时才会使用 server 中的 root 配置。

 

3、alias

如下:

3.1、root 和 alias 的区别

root 和 alias 的区别在于 nginx 如何解释 location 后面的 uri,这会使两者分别以不同的方式将请求映射到服务器文件上。

  • root 的处理结果是:root路径 + location路径
  • alias 的处理结果是:使用 alias 路径替换 location 路径

alias是一个目录别名的定义,root则是最上层目录的定义。还有一个重要的区别是 alias 后面必须要用 “/” 结束,否则会找不到文件的,而 root 则可有可无。

当我们使用 root 指定资源的路径时,root 会将 location 后代的路径完整保留,映射进文件路径中。而 alias 会先过滤掉 location 后面的路径,然后再将 alias 路径 + 访问资源路径,就是最后所访问的资源路径。

比如:

# root 配置如下。如果此时一个请求的URI是 /t/a.html 时,web服务器将会返回服务器上的/www/root/html/t/a.html的文件。
location ^~ /t/ {
    root /www/root/html/;
}

# alias 配置如下。如果此时一个请求的URI是/t/a.html时,web服务器将会返回服务器上的/www/root/html/new_t/a.html的文件。
# 注意这里是new_t,因为alias会把location后面配置的路径丢弃掉,把当前匹配到的目录指向到指定的目录。
location ^~ /t/ {
    alias /www/root/html/new_t/;
}

使用alias时,目录名后面必须要加 "/"。alias只能位于location块中,root可以不放在 location 中。

alias 在使用正则匹配时,必须捕捉要匹配的内容并在指定的内容处使用。比如:

location ~ /mytest/(.*) {
      alias /usr/local/nginx/html/$1;
}

 

3、index(默认页面)

Nginx 可以在 location 中配置 index,index 配置的是网站初始页,也就是默认页面。该指令拥有默认值,index   index.html ,即如果没有给出index,默认初始页为index.html

比如配置如下:

server {
    listen       80;
    server_name  localhost;

    location /webTestProject/ {
        root   /usr/myTestData/;
        index  index.html index.htm;
    }
}

我们可以直接访问 http://ip//webTestProject/,会默认命中服务器中的 /usr/myTestData/webTestProject/index.html 资源。

 

4、proxy_pass

语法规则:

proxy_pass URL;      #URL必须以http://或者https://开头

proxy_pass 的作用域在 location。特点如下:

  1. 不影响浏览器地址栏的url
  2. 设置被代理server的协议和地址
  3. 协议可以为http或https
  4. 地址可以为域名或IP

 

4.1、proxy_pass 后面的路径带 / 和不带的区别

在 proxy_pass 中的代理 url 后加上 /,代理转发的 url 中就不会带上 location 中匹配路径(注意,只是不带location中匹配的,即只是截取掉location中指定的,剩下的还是会带到代理转发的 url 中)。如果后面没有/,代理转发的 url 中就会带上 location 中的匹配路径。

示例如下:

# url 后带 /(则不会加上location中的匹配路径)
# 下面我们访问 http://ip/proxy/home.html,最终会访问到 http://myIp/home.html
location /proxy/ { proxy_pass http://myIp/; } # url中不带 /(则会加上location中的匹配路径)
# 下面我们访问 http://ip/proxy/home.html,最终会访问到http://myIp/proxy/home.html。这里会将 location 中匹配的 proxy 也自动加到代理转发的地址后面
location /proxy/ { proxy_pass http://myIp; }

 

代理转发的地址后面如果还带目录,此时最后面有没有 "/" 也不一样。

详情如下:

# 代理转发的地址后面带目录和 /
# 此时我们访问 http://ip/proxy/index.html,最终会访问到http://myIp/myFolder/index.html
location /proxy/ {
     proxy_pass http://myIp/myFolder/;
}

# 代理转发的地址后面带目录但没有 /
# 此时我们访问 http://ip/proxy/index.html,最终会访问到http://myIp/myFolderindex.html,比较奇怪
location /proxy/ {
     proxy_pass http://myIp/myFolder;
}

可参考:https://www.cnblogs.com/bigberg/p/7651197.html

 

4.2、proxy_cookie_path(修改cookie作用域)

语法结构:

proxy_cookie_path path replacement;   #将cookie的作用域由path改为replacement

在使用代理转发时,可能会发生 cookie 丢失的问题。

比如代理转发配置如下:

location /aaa/ {
     proxy_pass http://myIp/;
}

此时我们访问 http://ip/aaa/project/sigin.do,Nginx 会转发至 http://myIp/project/sigin.do,假设我们通过 sigin 接口来登录,并且返回 cookie,在该 cookie 里面存放着登录信息。登录过后我们通过 http://ip/aaa/project/getUser.do 来继续请求接口,此时你可能会发现后端拿不到 cookie 信息。

这是因为 sigin.do 接口返回的 cookie 的作用域会是 /project,而我们通过 http://ip/aaa/project/getUser.do 来发出接口浏览器是不会带上 cookie,因为 cookie 的作用域不是 /aaa。此时我们应该修改配置如下:

location /aaa/ {
     proxy_pass http://myIp/;
     proxy_cookie_path  /project /aaa;  #将cookie的作用域由/project改为/aaa
}

可参考:https://blog.csdn.net/isyoungboy/article/details/81382193 

 

5、rewrite(重定向)

基本语法结构如下:

rewrite regex replacement [flag];    # regex:指定需要匹配的URI的正则表达式   replacement:将正则表达式匹配到的内容替换成replacement  flag:标记,可不加

该指令就是通过正则表达式的使用来更改浏览器的 url 。也就是当匹配到指定的正则表达式后,会将 replacement 作为一个新的 URI,组成一个新的 URL 返回给客户端,客户端会自动进行重定向即自动请求返回的新的 URL。

示例如下:

# 此时不管我们访问 http://ip/aaa/bbb/ 或者是 http://ip/ccc/aaa/bbb/ ,浏览器都会重定向到 http://ip/portal/。也就是会将replacement直接作为新的URI返回给客户端
location ~ /aaa/bbb/ {
     rewrite /aaa/  /portal/  permanent;
}

如果 replacement 是以 http://、https:// 或 $scheme 开头的字符串,则处理流程会立即停止并将 replacement  作为新的 URL 返回并重定向客户端。

 

rewrite 只能放在 server{},、location{}、if{} 块中。默认情况下,rewrite 会将旧的 URL 的请求参数(也就是 ?符号之后的参数)也拼接到新的 URL 后面,如果我们不希望这么做,可以在 replacement 的最后面添加一个 ? 符号。当然,replacement 本身也是可以写请求参数的。

# 此时我们访问 http://ip/aaa/bbb/?name=wen ,浏览器会重定向到 http://ip/portal/?name=wen
location ~ /aaa/bbb/ {
     rewrite /aaa/  /portal/  permanent;
}

# 如果不希望默认拼接旧的url的请求参数,则可以在replacement的最后面加一个?符号,此时重定向后的url就不会拼接旧的url的请求参数
# 下面我们访问 http://ip/aaa/bbb/?name=wen ,浏览器会重定向到 http://ip/portal?myage=12
location ~ /aaa/bbb/ {
     rewrite /aaa/  /portal?myage=12?  permanent;
}

 

可以同时存在一个或多个 rewrite 指令,会按照在配置文件中出现的顺序依次对 URL 进行匹配和处理。

当 rewrite 写在 location 里时,它们的执行顺序是:执行 server 块的 rewrite 指令  -->  执行 location 匹配  -->  执行选定的 location 中的 rewrite 指令。如果在某步中 URI 被重写了,则会重新循环执行1-3,直到找到真实存在的文件为之;循环超过10次,则会返回 500 Internal Server Error错误。所以当 rewrite 写在 location 里时,最好要用 break 作为标记,否则可能会发生上述错误。

如果正则表达式有出现 } 或 ; 字符,则整个正则表达式应使用单引号 ' 或双引号 " 括起来。

可参考:https://www.cnblogs.com/tugenhua0707/p/10798762.html

 

5.1、flag(标记)

flag 有如下值:

  1. last:本条规则匹配完成后,不会再执行后面的 rewrite 指令,但会根据新的 URI 来匹配新的 location,然后可能再继续执行该 location 下的 rewrite 指令。(不常用)
  2. break:本条规则匹配完成即终止,新的 URI 也不会再匹配后面的任何规则。(不常用)。
  3. redirect:返回 302 临时重定向,浏览器地址会显示跳转后的新的URL地址。
  4. permanent:返回 301 永久重定向,浏览器地址会显示跳转后的新的URL地址。
这里 last 和 break 区别有点难以理解:
  1. last 一般写在 server 和 if 中,而 break 一般使用在 location 中
  2. last 不终止重写后的url匹配,即新的url会再从server走一遍匹配流程,而break终止重写后的匹配
  3. break 和 last 都能继续执行 rewrite 指令的后面的指令?

因为 301 和 302 不能只简单地返回状态码,还必须有重定向的URL,这就是 return 指令无法直接返回301、302的原因了。

 

6、return 指令

停止处理请求,直接返回状态码或重定向到其他 URL。执行 return 指令后,location 中后续指令将不会被执行。

# 语法
return code [text];
return code [URL];
return URL;

上下文为server、location、if。

 

7、server_name

server name 为虚拟服务器的识别标志,匹配到特定的server块,转发到对应的应用服务器中去,如:server_name 127.0.0.1 、 localhost 、域名[www.itcast.cn、www.itheima.com]。用户通过不同的域名来访问资源的话,Nginx 会获取请求头中的 HOST 字段,并根据 host 字段来匹配到特定的 server 块,转发到对应的应用服务器中去。

server_name 的匹配规则优先级如下:

  1. 精准匹配。(如 server_name www.test.com)
  2. 通配符在前的。(如server_name *.test.com)
  3. 通配符在后的。(如 www.test.*)
  4. 正则匹配。(如 ~ ^\.www\.test\.com$)

如果都不匹配,则:

  1. 优先选择 listen 配置项后有 default 或 default_server 的 server 块
  2. 找到匹配 listen 对应端口的第一个 server 块

 

(注意,server_name _ (即一个下划线)表示匹配所有域名,即只要通过该域名能访问到该服务器,则都能匹配到该 server 块; server_name "" 会匹配没有传递Host头部的情况。)

 

7.1、案例解析

配置文件如下:

server {
    listen 80;
    server_name www;
    location / {
        default_type text/html;
        content_by_lua '
            ngx.say("<p>first</p>")
        ';
    }
}
 
server {
    listen  80;
    server_name www.zkh.com;
    location / {
        default_type text/html;
        content_by_lua '
            ngx.say("<p>second</p>")
        ';        
    }
}
 
server {
    listen 80;
    server_name www.zkh.*;
    location / {
        default_type text/html;
        content_by_lua '
            ngx.say("<p>third</p>")
        ';
 
    }
}
 
server {
    listen 80;
    server_name ~\w+.com;
    location / {
        default_type text/html;
        content_by_lua '
            ngx.say("<p>forth</p>")
        ';        
    }
}
 
server {
    listen 80;
    server_name ~.*zkh.com;
    location / {
        default_type text/html;
        content_by_lua '
            ngx.say("<p>fifth</p>")
        ';
    }
}

修改hosts文件:

118.126.100.138 www.zkh.com
118.126.100.138 www.zkh.org
118.126.100.138 zkh.com
118.126.100.138 zkh.org

通过不同域名访问 Nginx,返回结果如下:

 

 参考:https://blog.csdn.net/Cheng_Kohui/article/details/82930464

 

8、内置变量

常见内置变量,参考:https://www.cnblogs.com/sxy-blog/p/17463264.html

 

8.1、如何修改内置变量

许多内建变量都是只读的,比如 $uri 和 $request_uri,对只读变量进行赋值是应当绝对避免的,如果你尝试改写另外一些只读的内建变量,比如 $arg_XXX 变量,在某些 Nginx 的版本中甚至可能导致进程崩溃。

也有一些内建变量是支持改写的,其中一个例子是 $args. 这个变量在读取时返回当前请求的 URL 参数串(即请求 URL 中问号后面的部分,如果有的话 ),而在赋值时可以直接修改参数串。我们来看一个例子:

location /test {
        set $orig_args $args;
        set $args "a=3&b=4";
 
        echo "original args: $orig_args";
        echo "args: $args";
}

这里我们把原始的 URL 参数串先保存在 $orig_args 变量中,然后通过改写 $args 变量来修改当前的 URL 参数串,最后我们用 echo 指令分别输出 $orig_args 和 $args 变量的值。

接下来我们这样来测试这个 /test 接口:

   $ curl 'http://localhost:8080/test'
    original args: 
    args: a=3&b=4
 
    $ curl 'http://localhost:8080/test?a=0&b=1&c=2'
    original args: a=0&b=1&c=2
    args: a=3&b=4

在第一次测试中,我们没有设置任何 URL 参数串,所以输出 $orig_args 变量的值时便得到空。而在第一次和第二次测试中,无论我们是否提供 URL 参数串,参数串都会在 location /test 中被强行改写成 a=3&b=4

 

8.2、内置变量 $remote_addr

内置变量 $remote_addr 指的是客户端地址,也就是发起请求者的客户端 ip 地址。

 

8.3、内置变量 $http_x_forwarded_for

内置变量 $http_x_forwarded_for 指的是请求头中的 x-forwarded-for。默认情况下,浏览器发出请求的请求头中是不含 x-forwarded-for 的,所以默认情况下在 Nginx 日志记录的 $http_x_forwarded_for 是空的。当然我们也可以手动指定发送 x-forwarded-for  请求头。

比如 Nginx 日志配置如下:

log_format  main  '客户端IP:$remote_addr <===> X-Forwarded-For请求头:$http_x_forwarded_for';
 
access_log  logs/access.log  main;

我们通过浏览器发出请求,如下,可以看到请求头中是不含 x-forwarded-for 信息的。

此时 Nginx 日志输出如下,可以看到能获取到客户端 ip,但是获取到的 x-forwarded-for 信息是空的。

 

要想Nginx获取到 x-forwarded-for 信息的,我们只需在发送请求时指定  x-forwarded-for(不区分大小写) 请求头即可。比如通过 postman 发送请求,如下:

此时 Nginx 日志输出如下,可以看到能正常获取到 x-forwarded-for 信息。

 

8.3、内置变量 $proxy_add_x_forwarded_for

$proxy_add_x_forwarded_for 变量 = 请求头X-Forwarded-For,(如果有的话) $remote_addr变量

即表示请求头 X-Forwarded-For 加上 $remote_addr变量的值。如果X-Forwarded-For字段没出现在客户端请求头,$proxy_add_x_forwarded_for 等同于$remote_addr 变量。

$proxy_add_x_forwarded_for 变量的值如果包含多个地址,用逗号+空格分隔,格式如下:

clientIP, proxyIP1, proxyIP2 # 最左边的clientIp一般是客户端真实IP

# 举例
22.12.3.20, 192.168.118.129, 192.168.118.130

 

9、ngx_http_realip_module 模块(real_ip_header)

9.1、详解

realip 功能用途:可以利用 realip 模块将用户的真实 ip 赋值到 $remote_addr 变量中以此获取到用户的真实IP地址。realip 的set_real_ip_fromreal_ip_header、real_ip_recursive 命令都可以用于 http、 serverlocation 区域配置。

realip 语法解释:

  • set_real_ip_from:设置信任服务器IP(一般设置为代理服务器的ip)。可以定义多行,可定义为ip,ip段,支持ipv4和ipv6。(注意,如果发送方的ip不在此信任名单中,realip模块则不处理,$remote_addr就还会是发送发的地址。如果ip在信任中,则会根据下一个参数real_ip_header的设定,将指定的header头字段的数据作为$remote_addr。)
  • real_ip_header 请求头:判定用户真实IP存在某个请求头中。比如 real_ip_header X-Forwarded-For,注意添加了此配置后,realip 模块并不会自动往指定的请求头如X-Forwarded-For中添加任何信息,所以说 realip 模块实际上是需要跟其他配置配合使用的,如 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  • real_ip_recursive:
    •   off:会将real_ip_header指定的HTTP头中的最后一个IP作为真实IP,并赋值给 $remote_addr
    •   on:会将real_ip_header指定的HTTP头中的最后一个不属于信任服务器的IP内的 ip 作为真实IP,并赋值给 $remote_addr

使用 realip 模块可以获取用户真实 ip,大致流程是先判定用户真实 ip 会放在请求头的哪个字段中(比如X-Forwarded-For),然后从该字段中剔除信任 ip(实际上可以理解为代理服务器的 ip),剩下的就是用户真实 ip。

假设架构如下:

此时我们可以在 130 代理服务器中做以下配置:

...
http {
    proxy_set_header Host $host;

    upstream rtpserver{
        server 192.168.118.131;
    }

    log_format  main  'remote_addr 值:$remote_addr <===> X-Forwarded-For请求头:$http_x_forwarded_for <===> realip_remote_addr数据:$realip_remote_addr';
    access_log  logs/access.log  main;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; server { listen 8090; server_name localhost;
...
location /portal/ { proxy_pass http://rtpserver/; } } }

然后在 131 服务器做以下配置,即指定上一级代理服务器中存放用户真实 ip 的请求头是什么,然后指定信任的 ip(实际上就是所有的代理服务器ip),131在接收到指定的请求头数据后,会剔除掉信任的ip,剩下的就是用户真实 ip。

...
http {
   ...
    
    log_format  main  '131服务器 => remote_addr 值:$remote_addr <===> X-Forwarded-For请求头:$http_x_forwarded_for <===> realip_remote_addr数据:$realip_remote_addr';
    access_log  logs/access.log  main;

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 下面三行为重点,添加后就可以获取到客户端真实IP
    set_real_ip_from 192.168.118.0/24;
    real_ip_header  X-Forwarded-For;
    real_ip_recursive on;

    server {
        listen       80;
        server_name  localhost;
        ...
    }
}

我们在 postman 中发出请求,如下:

130 服务器日志输出如下,

131服务器输出如下,可以看到 131 服务器中 $remote_addr 变量的值已被自动赋值为用户真实 ip,说明我们获取用户真实 ip 成功了。

 

参考:https://cloud.tencent.com/developer/article/1521273https://amos-x.com/index.php/amos/archives/nginx-realip/

 

9.2、解决配置 realip 模块报错问题

使用 realip 功能需要 Nginx 添加 ngx_http_realip_module 模块,默认情况下是不被编译。如果我们没有配置 realip,而在 Nginx 配置中使用 real_ip_header 的配置,则可能会报以下错误:

此时可参考:https://blog.csdn.net/qq_33101675/article/details/79013248。注意,链接中方法是重新安装了一个 Nginx,链接中的方法指定了安装的新的 Nginx 在 /usr/cmcc/nginx/conf 目录下,所以我们应该使用该目录下的 Nginx,如果我们还用旧的 Nginx 的话可能会报同样的错误。

realip模块的作用是:当本机的nginx处于一个反向代理的后端时获取到真实的用户IP。

如果没有realip模块,nginx的access_log里记录的IP会是反向代理服务器的IP,PHP中$_SERVER[‘REMOTE_ADDR’]的值也是反向代理的IP。而安装了realip模块,并且配置正确,就可以让nginx日志和php的REMOTE_ADDR都变成真实的用户IP。

 

posted @ 2021-08-03 23:52  wenxuehai  阅读(2074)  评论(0编辑  收藏  举报
//右下角添加目录