nginx透传X-Forwarded-Proto

背景

业务接入https,后端服务需要需要客户端的请求schma是老版本http,还是新版的https请求。这里通常采用的方案是通过X-Forwarded-Proto头来识别:

X-Forwarded-Proto (XFP) 是一个事实上的标准首部,用来确定客户端与代理服务器或者负载均衡服务器之间的连接所采用的传输协议(HTTP 或 HTTPS)。在服务器的访问日志中记录的是负载均衡服务器与服务器之间的连接所使用的传输协议,而非客户端与负载均衡服务器之间所使用的协议。为了确定客户端与负载均衡服务器之间所使用的协议, X-Forwarded-Proto 就派上了用场。

通常在ng中通过设置XFP来指定URI的scheme信息,如:

proxy_set_header X-Forwarded-Proto $scheme;

然后后端可以通过读取header X-Forwarded-Proto来获得对应的scheme。

但是现网的接入环境会比较复杂,通常存在多层反向代理的情况,这种简单粗暴的方式就不灵验了。
一个比较常见的https请求路径如下:

User-Agent —[https]—> haproxy(公司级接入) —[http]—> nginx(业务级接入) —[http]—> apache/php(后端服务接入)

最外层的接入收到User-Agent的https请求,但在内部和nginx的交互协议为http,所以nginx取到的scheme值就是http了,而非原始的https。 php(后端)也就无法知道真实的schema了。

解决方案

修改配置如下(以ng为例):

map $http_x_forwarded_proto $thescheme {
default $scheme;
https https;
}
proxy_set_header X-Forwarded-Proto $thescheme;

与之前的解决方案不同,不是将XFP设置为固定的scheme值,而是通过计算得出scheme。
map配置的意思是先判断是否已经存在XFP的header信息,如果是https则透传,否则使用连接的scheme。在最外层的反向代理中,没有XFP的header信息,所以会写入request,后续的接入层会逐步透传,一直到后端服务。

map directives

用法

Syntax: map string $variable { … }
Default: —
Context: http

功能:创建一个新的结果变量variable,其值依赖于源变量string。map block内指定了源变量值和结果值的映射关系。

性能

参考连接:

mapping条目 直接访问(rps) map第一条url(rps) map最后一条url(rps) 不存在的url(rps)
100 2829.44 1819.63 1765.25 9740.53
1000 - 1816.00 1509.52 4094.68
10000 - 1813.22 514.24 658.32
100000 - 1836.02 62.40 65.80

跟预想的一样,mapping的条目确实会对请求效率产生影响。而且几万条的映射在较高并发的情况下已经到了勉强能用的临界了。
在并发不是很高的时候mapping的条目可以更多。100000个条目大概只会影响整个请求15ms左右,可以忽略不计。如果说150ms的延迟是可以接受的,那么在一个并发不是很高的情况下,mapping最多可以有100w条,还是很多的