一、配置
http {
server {
...
location /http {
proxy_pass http://http_backend;
proxy_redirect default;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ignore_headers Set-Cookie;
proxy_hide_header Set-Cookie;
proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;
...
}
}
}
二、说明
1、proxy_pass
真实服务器
2、proxy_redirect
指定是否修改被代理服务器返回的响应头中的location头域跟refresh头域数值
3、proxy_set_header
重新定义或者添加发往后端服务器的请求头
4、proxy_set_header X-Real-IP
启用客户端真实地址(否则日志中显示的是代理在访问网站)
5、proxy_set_header X-Forwarded-For
记录代理地址
6、proxy_connect_timeout
定义与代理服务器建立连接的超时时间。需要注意的是这个超时时间通常不能超过75秒,默认值60
7、proxy_send_timeout
后端服务器数据回传时间_就是在规定时间之内后端服务器必须传完所有的数据
8、proxy_read_timeout
nginx接收upstream(上游/真实) server数据超时, 默认60s, 如果连续的60s内没有收到1个字节, 连接关闭。像长连接
9、Set-Cookie
在默认情况,nginx不缓存从后端响应的http头中带有Set-Cookie的对象。如果客户端发送的请求带有Cookie header,varnish将忽略
缓存,直接将请求传递到后端。nginx中通过proxy_ignore_headers设置忽略它们,设置方法如下:
解决办法:
proxy_ignore_headers Set-Cookie;
proxy_hide_header Set-Cookie;
10
# 有时候后端需要知道客户端是用的http请求还是https请求,加上一个X-Forwarded-Proto头
proxy_set_header X-Forwarded-Proto $scheme; # 禅道开启https,需要这个选项才能正确转发
额外说明
proxy_set_header
用来设定被代理服务器接收到的header信息。
语法:proxy_set_header field value;
field :为要更改的项目,也可以理解为变量的名字,比如host
value :为变量的值
如果不设置proxy_set_header,则默认host的值为proxy_pass后面跟的那个域名或者IP(一般写IP)
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
用来设置被代理端接收到的远程客户端IP,如果不设置,则header信息中并不会透传远程真实客户端的IP地址。
$proxy_host 自然是 proxy_pass后面跟着的host 比如一个请求 https://zhidao.baidu.com/question/22908463.html
Request.ServerVariables("HTTP_HOST") 可以获得HTTP_HOST请求标题:zhidao.baidu.com
$http_host始终等于HTTP_HOST请求标题
$host等于$http_host,小写并且没有端口号(如果存在),除非HTTP_HOST不存在或是空值。在这种情况下,$host等于server_name
$proxy_add_x_forwarded_for从客户访问到到最后的后台服务所经理的所有节点的ip汇总221.221.1.100,10.30.30.109,...
nginx的自带变量$remote_addr代表客户端的IP
$remote_addr代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的ip指定的,当你的浏览器访问某个网站时,假设
中间没有任何代理,那么网站的web服务器(Nginx,Apache等)就会把remote_addr设为你的机器IP,如果你用了某个代理,那么你的浏
览器会先访问这个代理,然后再由这个代理转发到网站,这样web服务器就会把remote_addr设为这台代理机器的IP。
$remote_addr 只能获取到与服务器本身直连的上层请求ip,所以设置$remote_addr一般都是设置第一个代理上面;但是问题是,有时候是通
过cdn访问过来的,那么后面web服务器获取到的,永远都是cdn 的ip 而非真是用户ip,那么这个时候就要用到X-Forwarded-For 了,这个
变量的意思,其实就像是链路反追踪,从客户的真实ip为起点,穿过多层级的proxy ,最终到达web 服务器,都会记录下来,所以在获取用
户真实ip的时候,一般就可以设置成,proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 这样就能获取所有的代理ip
客户ip。 $proxy_add_x_forwarded_for变量包含客户端请求头中的X-Forwarded-For与$remote_addr两部分,他们之间用逗号分开。
三、补充
1、proxy_set_header
经过反向代理后,由于在客户端和web服务器之间增加了中间层,因此web服务器无法直接拿到客户端的ip,通过$remote_addr变量拿到的将
是反向代理服务器的ip地址”。这句话的意思是说,当你使用了nginx反向服务器后,在web端使用request.getRemoteAddr()(本质上就是获
取$remote_addr),取得的是nginx的地址,即$remote_addr变量中封装的是nginx的地址,当然是没法获得用户的真实ip的,但是,nginx
是可以获得用户的真实ip的,也就是说nginx使用$remote_addr变量时获得的是用户的真实ip,如果我们想要在web端获得用户的真实ip,就
必须在nginx这里作一个赋值操作,如下:
proxy_set_header X-real-ip $remote_addr;
其中这个X-real-ip是一个自定义的变量名,名字可以随意取,这样做完之后,用户的真实ip就被放在X-real-ip这个变量里了,然后,在
web端可以这样获取:
request.getHeader("X-real-ip")
客户端地址(请求服务的地址):192.168.1.1
nignx服务器地址:192.168.1.2
后端服务器地址:192.168.1.3
首先说明proxy_set_header是用来设置请求头的,设置了请求头后,后端服务器就可以获取到这些变量值。
一、X-Real-IP
是指客户端的真实IP,如果设置了$remote_addr这个值,后端服务器就能获取到客户端的真实IP,也就是此例中的192.168.1.1
二、Host
host的值设置为$proxyhost,是指proxy_pass中设置的host值,也就是192.168.1.3,也就是服务器的IP地址。
若客户端发过来的请求header中有HOST这个字段,$http_host和$host表示的就是原始请求host,比如请求的时候HOST的值是
http://test.com,那么反代后还是http://test.com。
若客户端发过来的请求header中没有HOST这个字段,$host表示nginx代理服务器的地址,也就是此例中的192.168.1.2。
$httphost不是一个固定的变量,他其实是$http_HEADER通配后的结果,这里的HEADER是一个通配符,通配的是请求头里的header属性,例
如$http_content_type表示请求头里content-type属性的值,同理,$http_host指的就是请求头里的host属性。
三、X-Forwarded-For
这个变量的值有$proxy_add_x_forwarded_for和$remote_addr,在只有一个代理服务器的转发的情况下,两者的效果貌似差不多,都可以
真实的显示出客户端原始ip。
举例说明,用户A的IP是192.168.1.1,请求一个经过两次nginx转发的应用,在第一台nginx中(192.168.1.2),配置如下:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在$proxy_add_x_forwarded_for变量的"X-Forwarded-For"部分是空的,所以只有$remote_addr,而$remote_addr的值是用户的ip,那
么X-Forwarded-For变量的值就是用户的ip:192.168.1.1。
到第二台nginx,配置如下:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在的$proxy_add_x_forwarded_for变量,X-Forwarded-For部分包含的是用户的真实ip,$remote_addr部分的值是上一台nginx的ip地
址,那么X-Forwarded-For的值就变成了"用户的真实ip,第一台nginx的ip",也就是“192.168.1.1, 192.168.1.2”
所以还是建议X-Forwarded-For的值设置成$proxy_add_x_forwarded_for
2、proxy_redirect重定向url
1、用大白话来说这个参数就是,当客户端访问nginx,nginx访问后端,后端如果返回一个url,这个url肯定是包含后端的ip端口或者域名端口信息
当后端返回的url到达nginx以后,nginx将url中的ip端口或者域名端口转换成自身的再发送给客户端
客户端需要的一切url的访问都应该是nginx的,然后交给nginx去访问后端
在使用Nginx做反向代理功能时,有时会出现重定向的url不是我们想要的url,这时候就可以使用proxy_redirect进行url重定向设置了。
proxy_redirect功能比较强大,其作用是对发送给客户端的URL进行修改!!
2、语法:proxy_redirect [ default|off|redirect replacement ];
默认:proxy_redirect default;
配置块(使用的字段):http、server、location
当上游服务器返回的响应是重定向或刷新请求(如HTTP响应码是301或者302)时,proxy_redirect可以重设HTTP头部的location或refresh字段。
location /login {
proxy_pass http://target_servers/login ;
}
如果需要修改从被代理服务器传来的应答头中的"Location"和"Refresh"字段,这时候就可以用proxy_redirect这个指令设置。
假设被代理服务器返回Location字段为http://localhost:8000/kevin/some/uri/
proxy_redirect http://localhost:8000/kevin/ http://frontend/one/;
将Location字段重写为http://frontend/one/some/uri/。
在代替的字段中可以不写服务器名:
proxy_redirect http://localhost:8000/kevin/ /;
这样就使用服务器的基本名称和端口,即使它来自非80端口。
3、如果使用"default"参数,将根据location和proxy_pass参数的设置来决定。
下列两个配置等效:
location /one/ {
proxy_pass http://upstream:port/kevin/;
proxy_redirect default;
}
location /one/ {
proxy_pass http://upstream:port/kevin/;
proxy_redirect http://upstream:port/kevin/ /one/;
}
在指令中可以使用一些变量:
proxy_redirect http://localhost:8000/ http://$host:$server_port/;
4、这个指令有时可以重复:
proxy_redirect default;
proxy_redirect http://localhost:8000/ /;
5、参数off将在这个字段中禁止所有的proxy_redirect指令:
proxy_redirect off;
6、假设当前nginx的访问地址为http://10.0.9:8080,如果kevin-inc又需要302到10.0.9/xxx
那么可以添加下redirect,将302的location改为http://10.0.9:8080/xxx
location /login {
proxy_pass http://kevin-inc/login ;
proxy_redirect http://10.0.9/ http://10.0.9:8080/;
}
--------------------------------
host变量
如果不想写死ip地址,可以使用nginx的变量
location /login {
proxy_pass http://kevin-inc/login ;
proxy_redirect http://$host/ http://$http_host/;
}
其中host不带端口的,也就是nginx部署的主机ip,而$http_host是带端口的
7、
server {
listen 80;
server_name www.kevin.com;
location / {
proxy_pass http://10.0.8.40:9080;
}
}
这段配置一般情况下都正常,但偶尔会出错, 抓包发现服务器给客户端的跳转指令里加了端口号,如Location: http://www.kevin.com:9080/abc.html 。
因为nginx服务器侦听的是80端口,所以这样的URL给了客户端,必然会出错.
针对这种情况, 加一条proxy_redirect指令: proxy_redirect http://www.kevin.com:9080/ / ,即把所有"http://www.kevin.com:9080/"的内容替换成
"/"再发给客户端,就解决了。
server {
listen 80;
server_name www.kevin.com;
proxy_redirect http://www.kevin.com:9080/ /;
location / {
proxy_pass http://10.0.8.40:9080;
}
}
8、如下启用了proxy_redirect配置(http->https),配置中就不需要"proxy_set_header Host $host;",即不需要"添加发往后端服务器的请求头"的配置了
upstream scf_cluster {
ip_hash;
server 192.168.10.20:9020;
server 192.168.10.21:9020;
}
server {
listen 443;
server_name www.kevin.com kevin.com;
ssl on;
ssl_certificate /data/nginx/conf/ssl/kevin.cer;
ssl_certificate_key /data/nginx/conf/ssl/kevin.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE;
ssl_prefer_server_ciphers on;
access_log /data/nginx/logs/www.kevin.com-access.log main;
error_log /data/nginx/logs/www.kevin.com-error.log;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location /scf {
proxy_pass http://scf_cluster/scf;
proxy_redirect http://scf_cluster/scf https://www.kevin.com/scf;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 600;
proxy_buffer_size 256k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_temp_file_write_size 256k;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404;
proxy_max_temp_file_size 128m;
}
}
9、在看下nginx中非80端口的转发,注意:当端口是非80时,proxy_set_header项的$host后面一定要加上端口
如下,当http通过proxy_pass到非80端口的做法:
upstream mobi_cluster{
server 10.0.54.20:8080;
}
server {
listen 80;
server_name mobi.kevin.com;
access_log /data/nginx/logs/mobi.kevin.com-access.log main;
error_log /data/nginx/logs/mobi.kevin.com-error.log;
location / {
proxy_pass http://mobi_cluster;
proxy_set_header Host $host;
proxy_redirect http://mobi_cluster/ http://mobi.kevin.com/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}