前言
业务野蛮生长时期,作为一枚op,有运营过nginx比较长的一段时间。期间遇到些小问题,这里简单做个总结记录,会不定时更新:
开始扯淡
proxy_set_header的作用域限制
还记得是个后台鉴权server需要调用方提供一个QC_REAL_IP的http头做校验,我在nginx里配置如下:
proxy_set_header QC_REAL_IP $http_host;
location ^~/authorize/ {
proxy_set_header Host $http_host;
proxy_pass http://authorize_server;
proxy_redirect off;
}
但是,却发现鉴权不成功,一直是返回503。掏出tcpdump抓包
tcpdump -i eth1 -nn -Als0p 'tcp and host author_server_ip and port author_server_port'
发现nginx发出的包头里没有QC_REAL_IP字段,遂翻阅nginx wiki,找到这行
proxy_set_header directives issued at higher levels are only inherited when no proxy_set_header directives have been issued at a given level.
原来proxy_set_header不完全是继承关系!把QC_REAL_IP的配置copy一份到location里,终于生效了。
https业务proxy_pass的HOST头设置
刚开始拿nginx做proxy,一般proxy_pass和upstream的名字一致,有多个proxy_pass就加数字.例如:
upstream xxoo.com {
server 10.8.8.8:8080;
}
upstream xxoo.com_2 {
server 10.8.8.8:8088;
}
server {
listen 443;
server_name xxoo.com
... ...
location /api/user/ {
proxy_pass http://xxoo.com;
proxy_redirect off;
}
location /api/pay/ {
proxy_pass http://xxoo.com_2;
proxy_redirect off;
}
}
重启nginx后,访问测试pay接口第一个https访问是正常的,但由后端server返回的302跳转全部变成了http请求。在nginx和后端机器tcpdump抓包分析,发现pay接口从nginx过去的请求头Host是xxoo.com_2,而user接口是xxoo.com则302跳转https正常!
所以nginx的proxy模块请求backend的http header中Host字段指就是proxy_pass 指令的值。nginx根据该Host是否和server_name匹配绝对是否对前端返回https。
透传客户端ip
一般情况我在nginx接入侧配置这条足以:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_add_x_forwarded_for的值为: 客户端穿过来的x_forwarded_for值+ remote_addr。如果客户端传来的x-forwarded-for值本事是非法、错误的。那么后端应用如果截取左起第一个字段获取到的ip值也是错误。
这个一般有2个办法,首先,可以在nginx接入对很明显非法的ip摘掉,强制把remote_addr值添到X-Forwarded-For字段:
set $my_proxy_add_x_forwarded_for $proxy_add_x_forwarded_for;
if ($proxy_add_x_forwarded_for ~* "127.0.0.1"){
set $my_proxy_add_x_forwarded_for $remote_addr;
}
proxy_set_header X-Forwarded-For $my_proxy_add_x_forwarded_for;
另外,如果是做ip频率限制、校验的话,还不如直接取X-Forwarded-For值的最后一段。因为伪造x-forword-for值成本太低了。
404返回码转换
有个很老的接口拉取资料返回给客户端,不时的出现404,但对业务没影响。开发人力不足以重构这个接口,为了不影响调用成功率,想都设置为返回成功:200,302之类的状态码
location / {
error_page 404 =302 /404.html;
}
记录慢日志
为提高api接口的运营质量,同时也方便定位一些奇怪的问题。需要记录nginx的慢日志,有个简单粗暴的办法,利用nginx+lua直接打日志。
log_by_lua '
if tonumber(ngx.var.upstream_response_time) > 3 then
local status_code = ngx.var.upstream_status
local time_ts= ngx.localtime()
local upstream_res_time= ngx.var.upstream_response_time
ngx.log(ngx.ERR, "[SLOW] upstream_responce_time:" .. upstream_res_time .. ", upstream_http_code:" .. status_code ..",");
end
';
跨域设置CORS
这个没啥技巧,为方便前端开发,在接入层打开跨域资源共享。但从安全角度考虑,不能完全打开限制,所以对业务的几个referer域名做了正则匹配。
if ($http_referer ~ http://(.*)\.(xx1|xx2|xx3)\.(com|cn)(/*.*)){
add_header Access-Control-Allow-Credentials true;
add_header Timing-Allow-Origin http://$1.$2.$3;
add_header Access-Control-Allow-Origin http://$1.$2.$3;
}