Nginx中$http_host、$host、$proxy_host的区别

知识巩固!

网上看到这篇文章,这里转载记录一下。

简介

|-------------------------|----------------|------------------------------|
| 变量 | 是否显示端口 | 值是否存在 |
| host 浏览器请求的ip,不显示端口 | 否 | "Host:value"显示 值为a:b的时候,只显示a |
| http_host 浏览器请求的ip和端口号 | 是 | "Host:value",value存在就显示 |
| proxy_host 被代理服务的ip和端口号 | 默认80不显示 其他端口显示 | "Host:value"显示 |

配置反向代理时,接口请求报404问题

应用描述:前端应用域名为A(ww.a.com), 后端服务域名为B(www.b.com); 为了解决跨域问题,配置nginx反向代理如下:

...

proxy_set_header Host $host;

...

location ^~ /api/ {

rewrite "^/api/(.*)$" /$1 break;

proxy_pass http://www.b.com;

}

问题:这样配置完成后,接口报404问题。

解决:

方案一:将proxy_set_header注释掉

方案二:修改反向代理配置,设置请求头Host,如下所示:

location ^~ /api/ {

rewrite "^/api/(.*)$" /$1 break;

proxy_pass http://www.b.com;
proxy_set_header Host $proxy_host; //方式一:设置请求头host为www.b.com的ip和端口号
proxy_set_header Host www.b.com; //方式二:设置请求host为www.b.com

}

nginx中proxy_set_header Host $host的作用

nginx为了实现反向代理的需求而增加了一个ngx_http_proxy_module模块。其中proxy_set_header指令就是该模块需要读取的配置文件。在这里,所有设置的值的含义和http请求体中的含义完全相同,除了Host外还有X-Forward-For。

Host的含义是表明请求的主机名,因为nginx作为反向代理使用,而如果后端真实服务器设置有类似防盗链或者根据http请求头中的host字段来进行路由或判断功能的话,如果反向代理层的nginx不重写请求头中的host字段,将会导致请求失败【默认反向代理服务器会向后端真实服务器发送请求,并且请求头中的host字段应为proxy_pass指令设置的服务器】

同理,X_Forward_For字段表示该条http请求是由谁发起的?如果反向代理服务器不重写该请求头的话,那么后端真实服务器在处理时会认为所有的请求都来在反向代理服务器,如果后端有防攻击策略的话,那么机器就被封掉了。因此,在配置用作反向代理的nginx中一般会增加两条配置,修改http的请求头。

|-----|------------------------------------------------------------------------------------|
| 1 2 | proxy_set_header Host $http_host; proxy_set_header X-Forward-For $remote_addr; |

这里的http_host和remote_addr都是nginx的导出变量,可以在配置文件中直接使用。如果Host请求头部没有出现在请求头中,则http_host值为空,但是host值为主域名。因此,一般而言,会用host代替http_host变量,从而避免http请求中丢失Host头部的情况下Host不被重写的失误。

X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP,只有在通过了HTTP 代理或者负载均衡服务器时才会添加该项。 它不是RFC中定义的标准请求头信息,在squid缓存代理服务器开发文档中可以找到该项的详细介绍。标准格式如下:X-Forwarded-For: client1, proxy1, proxy2。

这一HTTP头一般格式如下:X-Forwarded-For: client1, proxy1, proxy2。其中的值通过一个"逗号+空格"把多个IP地址区分开, 最左边(client1)是最原始客户端的IP地址, 代理服务器每成功收到一个请求,就把请求来源IP地址添加到右边。

在上面这个例子中,这个请求成功通过了三台代理服务器:proxy1, proxy2 及 proxy3。请求由client1发出,到达了proxy3(proxy3可能是请求的终点)。请求刚从client1中发出时,XFF是空的,请求被发往proxy1;通过proxy1的时候,client1被添加到XFF中,之后请求被发往proxy2;通过proxy2的时候,proxy1被添加到XFF中,之后请求被发往proxy3;通过proxy3时,proxy2被添加到XFF中,之后请求的的去向不明,如果proxy3不是请求终点,请求会被继续转发。

鉴于伪造这一字段非常容易,应该谨慎使用X-Forwarded-For字段。正常情况下XFF中最后一个IP地址是最后一个代理服务器的IP地址, 这通常是一个比较可靠的信息来源。

proxy_set_header设置Host为proxy_host,host与$local_host的区别

先来看下proxy_set_header的语法:proxy_set_header ``field`` ``value``;

默认值:

|-----|---------------------------------------------------------------------------|
| 1 2 | proxy_set_header Host $proxy_host; proxy_set_header Connection close; |

上下文:http, server, location

作用:允许重新定义或者添加发往后端服务器的请求头。value可以包含文本、变量或者它们的组合。 当且仅当当前配置级别中没有定义proxy_set_header指令时,会从上面的级别继承配置。 默认情况下,只有两个请求头会被重新定义:

|-----|---------------------------------------------------------------------------|
| 1 2 | proxy_set_header Host $proxy_host; proxy_set_header Connection close; |

nginx对于upstream默认使用的是基于IP的转发,因此对于以下配置:

|----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | upstream backend { ``server 127.0.0.1:8080; ``} ``upstream crmtest { ``server crmtest.aty.sohuno.com; ``} ``server { ``listen 80; ``server_name chuan.aty.sohuno.com; ``proxy_set_header Host $http_host; ``proxy_set_header x-forwarded-for $remote_addr; ``proxy_buffer_size 64k; ``proxy_buffers 32 64k; ``charset utf-8; ``access_log logs/host.access.log main; ``location = /50x.html { ``root html; ``} ``location / { ``proxy_pass backend ; ``} ``location = /customer/straightcustomer/download { ``proxy_pass http://crmtest; ``proxy_set_header Host $proxy_host; ``} ``} |

当匹配到 /customer/straightcustomer/download时,使用crmtest处理,到upstream就匹配到crmtest.aty.sohuno.com,这里直接转换成IP进行转发了。假如crmtest.aty.sohuno.com是在另一台nginx下配置的,ip为10.22.10.116,则$proxy_host则对应为10.22.10.116。此时相当于设置了Host为10.22.10.116。

|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 | // 如果想让Host是crmtest.aty.sohuno.com,则进行如下设置: proxy_set_header Host crmtest.aty.sohuno.com; // 如果不想改变请求头"Host"的值,可以这样来设置: proxy_set_header Host $http_host; // 但是,如果客户端请求头中没有携带这个头部,那么传递到后端服务器的请求也不含这个头部。 // 这种情况下,更好的方式是使用$host变量------它的值在请求包含"Host"请求头时为"Host"字段的值,在请求未携带"Host"请求头时为虚拟主机的主域名: proxy_set_header Host $host; // 此外,服务器名可以和后端服务器的端口一起传送: proxy_set_header Host $host:$proxy_port; // 如果某个请求头的值为空,那么这个请求头将不会传送给后端服务器: proxy_set_header Accept-Encoding ""; |

通过Nginx配置演示:

复制代码
[root@ans3 conf]# cat nginx.conf
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
error_log  logs/error.log  info;
#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  logs/access.log  main;
    sendfile        on;
    tcp_nopush     on;
    keepalive_timeout  65;

    server {
       listen       80;
       server_name  a.test.com;

       location / {
            proxy_pass http://10.0.0.50:8080;
            proxy_set_header X-Proxy-Host $proxy_host;
            proxy_set_header Host $http_host;
            index index.html index.htm;
       }
    }
}

另一台服务器配置

复制代码
[root@master conf]# cat nginx.conf
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       8080;
        server_name  www.test.com aa.test.com;

        location / {
              return 200 'http_host=[$http_host] host=[$host] proxy_host=[$http_x_proxy_host]\n';
        }
    }
}

不携带请求头 Host

复制代码
[root@ans3 conf]# curl -H 'Host:' --http1.0 http://a.test.com

http_host=[] 
host=[www.test.com] 
proxy_host=[10.0.0.50:8080]
变量 说明
http_host 请求无 Host, 则 http_host 为空, 继而无 Host 传到 proxy
host www.test.com proxy 无 Host 传入, 则使用其 server_name 的第一项
proxy_host 10.0.0.50:8080 取自于 proxy_pass 的参数

携带请求头 Host

复制代码
[root@ans3 conf]# curl -H 'Host:abc:123' --http1.0 http://a.test.com

http_host=[abc:123] 
host=[abc] 
proxy_host=[10.0.0.50:8080]
变量 说明
http_host abc:123 给啥拿啥
host abc 第一个:前的内容(小写)
proxy_host 10.0.0.50:8080 带端口显示

修改真实服务器的端口为默认端口

复制代码
http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  logs/access.log  main;
    sendfile        on;
    tcp_nopush     on;
    keepalive_timeout  65;

    server {
        listen  80;
        server_name  a.test.com;
        
        location / {
            proxy_pass http://10.0.0.50:80;
            proxy_set_header X-Proxy-Host $proxy_host;
            proxy_set_header Host $http_host;
            index index.html index.htm;
       }
    }
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name www.test.com aa.test.com;


        location / {
             return 200 'http_host=[$http_host] host=[$host] proxy_host= [$http_x_proxy_host]\n';
       }
    }
}

访问时proxy_host会省略80端口

[root@ans3 conf]# curl -H 'Host:abc:123' --http1.0 http://a.test.com

http_host=[abc:123] 
host=[abc] 
proxy_host=[10.0.0.50]
相关推荐
李昊哲小课10 分钟前
deepin 安装 zookeeper
大数据·运维·zookeeper·debian·hbase
真真-真真26 分钟前
WebXR
linux·运维·服务器
wanhengidc1 小时前
短视频运营行业该如何选择服务器?
运维·服务器
雨中rain2 小时前
Linux -- 从抢票逻辑理解线程互斥
linux·运维·c++
-KamMinG2 小时前
Centos7.9安装openldap+phpldapadmin+grafana配置LDAP登录最详细步骤 亲测100%能行
运维·grafana
Bessssss2 小时前
centos日志管理,xiao整理
linux·运维·centos
豆是浪个2 小时前
Linux(Centos 7.6)yum源配置
linux·运维·centos
vvw&2 小时前
如何在 Ubuntu 22.04 上安装 Ansible 教程
linux·运维·服务器·ubuntu·开源·ansible·devops
王铁柱子哟-2 小时前
解决 正在下载VS Code 服务器... 问题
运维·服务器
chinayu20073 小时前
虚拟机桥接模式
linux·运维·桥接模式