nginx關(guān)于add_header的坑及解決
一、add_header指令不會(huì)去重
nginx做反向代理時(shí),如果后端返回的response中已經(jīng)有該header頭,則通過(guò)add_header后會(huì)返回給客戶端兩個(gè)同樣的header頭。
場(chǎng)景1:

nginxA作為反向代理,nginxB作為web服務(wù)。我是拿的openresty 1.13.6.2測(cè)試的,本質(zhì)上是一樣,其中A是openresty 1.15.8.1嗎,B是openresty 1.13.6.2。
其中 nginxA 的日志格式里配置了打印上游返回的Server頭: xes-app : $http_upstream_server
測(cè)試1: add_header指令對(duì)重復(fù)指令的處理。
A的location配置如下:
server {
listen 80;
server_name test.header.com;
access_log /home/nginx/logs/test.header.com_access.log main;
error_log /home/nginx/logs/error.log debug;
location / {
set $upstream 'test.header.com';
proxy_pass http://$upstream;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}nginxB的配置如下:
server{
listen 80;
server_name test.header.com;
access_log /home/nginx/logs/test.header.com.log main;
index index.php index.html index.htm;
location / {
add_header Server 'MytestB';
return 200 "this is test header from zlear";
}
}這種配置下,B返回給A響應(yīng)時(shí)會(huì)加上一個(gè)'Server: MytestB'的header。
- 直接訪問(wèn)B:

- 訪問(wèn)A,A反向代理到B:

此時(shí)只有一個(gè)Server的 header,而且是A的openresty 版本標(biāo)識(shí)。
測(cè)試1結(jié)果:
1 : nginx返回響應(yīng)時(shí),會(huì)自動(dòng)在http報(bào)文里加上了當(dāng)前自己的Server標(biāo)識(shí)。
2: 單純的配置add_header指令時(shí),如果response中已經(jīng)有該header了,則會(huì)重復(fù)添加。
3: 通過(guò)反向代理NginxA之后,A會(huì)隱藏掉B返回給自己的Server Header頭,并將自己的Server標(biāo)識(shí)返回給客戶端。
測(cè)試2: proxy_hide_header與proxy_pass_header指令對(duì)add_header有影響嗎?
說(shuō)明:proxy_hide_header : nginx在做反向代理時(shí),為了隱藏上游服務(wù)器的信息,不會(huì)將上游的Server返回給客戶端。
語(yǔ)法: proxy_hide_header field;
默認(rèn)值: —
上下文: http, server, location
nginx默認(rèn)不會(huì)將“Date”、“Server”、“X-Pad”,和“X-Accel-...”響應(yīng)頭發(fā)送給客戶端。proxy_hide_header指令則可以設(shè)置額外的響應(yīng)頭,這些響應(yīng)頭也不會(huì)發(fā)送給客戶端。
proxy_pass_header: 和proxy_hide_header相反,如果希望允許傳遞某些響應(yīng)頭給客戶端,可以使用proxy_pass_header指令。
例如: proxy_pass_header Server; 則告訴nginx服務(wù)傳遞上游的Server頭,而不是將它自己放在響應(yīng)中。
- 在測(cè)試1的基礎(chǔ)上,在A上加上proxy_pass_header指令:
server {
listen 80;
server_name test.header.com;
access_log /home/nginx/logs/test.header.com_access.log main;
error_log /home/nginx/logs/error.log debug;
location / {
set $upstream 'test.header.com';
proxy_pass http://$upstream;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_header Server;
}
}通過(guò)A訪問(wèn)B如下:

此時(shí)和測(cè)試1中的直接訪問(wèn)B結(jié)果一樣。
- 在測(cè)試1的基礎(chǔ)上,在NginxB上添加指令add_header MytestB MytestB;
server{
listen 80;
server_name test.header.com;
access_log /home/nginx/logs/test.header.com.log main;
index index.php index.html index.htm;
location / {
add_header Server 'MytestB';
add_header MytestB 'MytestB';
return 200 "this is test header from zlear";
}
}
測(cè)試2結(jié)果:
1、 nginx默認(rèn)會(huì)隱藏上游返回的Server 的header頭,但是可以通過(guò)pass_header_header Server;來(lái)取消該限制,此時(shí)用戶會(huì)收到B返回的兩個(gè)header頭。
2、對(duì)于其他的非默認(rèn)屏蔽的header頭,則NginxA會(huì)原樣透?jìng)鹘o用戶。如果想屏蔽某個(gè)header頭,可以通過(guò)proxy_hide_header指令。
二、add_header指令會(huì)覆蓋
如果在http、server、location都配置了add_header指令之后,返回給用戶的是什么呢?
例如如下配置:
server {
listen 80;
server_name test.header.com;
access_log /home/nginx/logs/test.header.com_access.log main;
error_log /home/nginx/logs/error.log debug;
add_header Test1 AAA;
location / {
set $upstream 'test.header.com';
proxy_pass http://$upstream;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header Test2 BBB;
}
}這樣的配置下,會(huì)返回給用戶什么header呢?
答案是只會(huì)以location的為準(zhǔn),在server里配置的Test1并不生效。同理在http段配置的更加不會(huì)生效。 優(yōu)先級(jí)location > server > http。
三、擴(kuò)展
1、 ngx.resp.get_headers()
對(duì)Nginx A測(cè)試 ngx.resp.get_headers
-- header.lua
local json = require('cjson')
local h, err = ngx.resp.get_headers()
ngx.log(ngx.ERR, '-------resp header:-------', json.encode(h))結(jié)果如下:
2020/08/06 10:07:02 [error] 5616#0: *15175 [lua] header.lua:9: -------resp header:-------{"connection":"keep-alive","content-type":"application\/octet-stream","mytestb":"MytestB","content-length":"34"} while reading response header from upstream, client: 127.0.0.1, server: test.header.com, request: "GET http://test.header.com/aaa HTTP/1.1", upstream: "http://xxxx:80/aaa", host: "test.header.com"
接著在A中加上配置項(xiàng): proxy_pass_header Server;
再次測(cè)試結(jié)果:
2020/08/06 10:04:58 [error] 5564#0: *14655 [lua] header.lua:9: -------resp header:-------{"content-type":"application\/octet-stream","server":["openresty\/1.13.6.2","MytestB"],"connection":"keep-alive","mytestb":"MytestB","content-length":"34"} while reading response header from upstream, client: 127.0.0.1, server: test.header.com, request: "GET http://test.header.com/aaa HTTP/1.1", upstream: "http://xxx:80/aaa", host: "test.header.com"
結(jié)論
1、ngx.resp.get_headers() 只能獲取到proxy_hide_header外的header頭,如果想獲取到默認(rèn)被屏蔽掉的那些header,需要用proxy_pass_header來(lái)添加。
2、 默認(rèn)情況下,此api獲取到的header是一個(gè)key-value形式,但是如果upstream返回了兩個(gè)同樣的header,lua會(huì)用數(shù)組的形式存儲(chǔ)。
2、 Server-tag會(huì)覆蓋Server header頭
后端B換成Tenginx,并且在nginx.conf中加上Server-tag: MytestB
則 B返回給A的響應(yīng)頭中,那個(gè)默認(rèn)的Server頭已經(jīng)由openresty/1.13.6.1換成了 MytestB,則A再記錄日志,返回給用戶都是zzt-Server
3、 代碼自定義Server的header頭會(huì)覆蓋nginx自帶的
后端B是golang服務(wù),golang代碼里加上Server頭,則同樣可以滿足2中的效果。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
單臺(tái)web服務(wù)器如何盡可能的提高網(wǎng)站性能
一個(gè)網(wǎng)站,對(duì)于個(gè)人或小公司來(lái)說(shuō),前期直接上集群的開(kāi)銷是比較大的,那么采用單臺(tái)服務(wù)器如何才能盡可能的提高網(wǎng)站效率呢?2014-06-06
Nginx?ingress?controller高可用的實(shí)現(xiàn)
本文主要介紹了Nginx?ingress?controller高可用的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
詳解Nginx proxy_pass的一個(gè)/斜杠引發(fā)的血案
這篇文章主要介紹了詳解Nginx proxy_pass的一個(gè)/斜杠引發(fā)的血案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Nginx如何進(jìn)行流量按比例轉(zhuǎn)發(fā)
Nginx 可以借助split_clients指令或通過(guò) weight 參數(shù)以及 Lua 腳本實(shí)現(xiàn)流量按比例轉(zhuǎn)發(fā),下面小編就為大家介紹一下兩種方式具體的操作步驟吧2025-03-03
nginx centos 服務(wù)開(kāi)機(jī)啟動(dòng)設(shè)置實(shí)例詳解
這篇文章主要介紹了nginx centos 服務(wù)開(kāi)機(jī)啟動(dòng)設(shè)置實(shí)例詳解的相關(guān)資料,這里對(duì)服務(wù)開(kāi)機(jī)啟動(dòng)做了詳細(xì)的步驟介紹,需要的朋友可以參考下2016-11-11
nginx實(shí)現(xiàn)多個(gè)域名和集群的方法步驟
本文主要介紹了nginx實(shí)現(xiàn)多個(gè)域名和集群的方法步驟,需要配置Nginx作為反向代理服務(wù)器,將來(lái)自不同域名的請(qǐng)求轉(zhuǎn)發(fā)到集群中的相應(yīng)后端服務(wù)器,感興趣的可以了解一下2024-04-04
nginx簡(jiǎn)單配置多個(gè)server的方法
這篇文章主要介紹了nginx簡(jiǎn)單配置多個(gè)server的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Nginx搭建負(fù)載均衡集群的實(shí)現(xiàn)
這篇文章主要介紹了Nginx搭建負(fù)載均衡集群的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03

