ArcherWong博客
首页
博客
nginx笔记-反向代理
作者:ArcherWong
分类:nginx
时间:2019-01-04 10:36:26
阅读:12
[TOC] # 1. 正向代理和反向代理 ## 1.1 正向代理 正向代理(Forward Proxy)通常都被简称为代理,就是在用户无法正常访问外部资源,比方说受到GFW的影响无法访问twitter的时候,我们可以通过代理的方式,让用户绕过防火墙,先访问代理服务器,然后告诉代理服务器去访问twitter,取到信息后返回给我们,从而连接到目标网络或者服务。。  ## 1.2 反向代理 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。  ## 1.3 正向代理和反向代理的区别 (http://markdown.archerwong.cn/2018-12-18-08-10-43_3.png)  正向代理中,proxy和client同属一个LAN,对server透明;反向代理中,proxy和server同属一个LAN,对client透明。 正向代理隐藏真实客户端,反向代理隐藏真实服务端 # 2. 反向代理的好处 ## 2.1 保护了真实的web服务器 web服务器对外不可见,外网只能看到反向代理服务器,而反向代理服务器上并没有真实数据,因此,保证了web服务器的资源安全,我们还可以在代理服务器层增加waf防火墙,这样可以保证发往真是服务器的请求更加安全。 ## 2.2 节约了有限的IP地址资源 企业内所有的网站共享一个在internet中注册的IP地址,这些服务器分配私有地址,采用虚拟主机的方式对外提供服务。 ## 2.3 减少WEB服务器压力,提高响应速度 反向代理就是通常所说的web服务器加速,它是一种通过在繁忙的web服务器和外部网络之间增加一个高速的web缓冲服务器来降低实际的web服务器的负载的一种技术。反向代理是针对web服务器提高加速功能,作为代理缓存,它并不是针对浏览器用户,而针对一台或多台特定的web服务器,它可以代理外部网络对内部网络的访问请求。 反向代理服务器会强制将外部网络对要代理的服务器的访问经过它,这样反向代理服务器负责接收客户端的请求,然后到源服务器上获取内容,把内容返回给用户,并把内容保存到本地,以便日后再收到同样的信息请求时,它会把本地缓存里的内容直接发给用户,以减少后端web服务器的压力,提高响应速度。因此Nginx还具有缓存功能。 ## 2.4 其他优点 (1)请求的统一控制,包括设置权限、过滤规则等; (2)区分动态和静态可缓存内容; (3)实现负载均衡,内部可以采用多台服务器来组成服务器集群,外部还是可以采用一个地址访问; (4)解决Ajax跨域问题; (5)作为真实服务器的缓冲,解决瞬间负载量大的问题; # 3. 应用场景 **反向代理的应用场景1:** 北邮的图书馆资源只能在校内访问,当一个同学暑期放假回家,想要查询图使馆资源的资料(比如知网),因为他不在校园内,所以他没有办法免费访问知网,此时如果有一个反向代理服务器(在校园内网,有权限免费访问知网),他就可以先访问反向代理服务器,然后让反向代理服务器去帮他和知网沟通。 **反向代理的应用场景2:** 反向代理实现负载均衡 ``` upstream myapp { server 192.168.0.100:8080; server 192.168.0.101:8080; server example.com:8080; } server { listen 80; location / { proxy_pass http://myapp; } } ``` 这样对外发布的还是一个ip地址,用户并不知道后面其实有3个真实的服务器,在处理请求,这样可以大大提高处理性能。 # 4. 反向代理的配置 基本配置很简单 ``` location / { proxy_pass http://zhengde.xxx.cn; proxy_set_header Host zhengde.xxx.cn; } ``` ## 4.1 proxy_pass proxy_pass 后面跟着一个 URL,用来将请求反向代理到 URL 参数指定的服务器上。例如我们上面例子中的 `proxy_pass http://zhengde.xxx.cn;`,则将匹配的请求反向代理到 http://zhengde.xxx.cn。 ## 4.2 proxy_set_header 语法: ``` proxy_set_header field value; ``` 默认值: ``` proxy_set_header Host $proxy_host; proxy_set_header Connection close; ``` 上下文: ``` http, server, location ``` 允许重新定义或者添加发往后端服务器的请求头。value可以包含文本、变量或者它们的组合。 当且仅当当前配置级别中没有定义proxy_set_header指令时,会从上面的级别继承配置。 默认情况下,只有两个请求头会被重新定义: ``` proxy_set_header Host $proxy_host; proxy_set_header Connection close; ``` 如果不想改变请求头“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 ""; ``` 默认情况下,反向代理不会转发**原始请求**中的 Host 头部,真实服务器可能会根据这个host找到相应的站点,不写的话可能找不到,如果需要转发,就需要加上这句:`proxy_set_header Host $host;` ## 4.3 proxy_cookie_domain 语法: ``` proxy_cookie_domain off; proxy_cookie_domain domain replacement; ``` 默认值: ``` proxy_cookie_domain off; ``` 上下文: ``` http, server, location ``` 这个指令出现在版本 1.1.15. 设置“Set-Cookie”响应头中的domain属性的替换文本。 假设后端服务器返回的“Set-Cookie”响应头含有属性“domain=localhost”,那么指令 ``` proxy_cookie_domain localhost example.org; ``` 将这个属性改写为“`domain=example.org`”。 domain和replacement配置字符串,以及domain属性中起始的点将被忽略。 匹配过程大小写不敏感。domain和replacement配置字符串中可以包含变量: 可以同时定义多条proxy_cookie_domain指令: ``` proxy_cookie_domain localhost example.org; proxy_cookie_domain ~\.([a-z]+\.[a-z]+)$ $1; ``` off参数可以取消当前配置级别的所有proxy_cookie_domain指令: ``` proxy_cookie_domain off; proxy_cookie_domain localhost example.org; proxy_cookie_domain www.example.org example.org; ``` ## 4.4 proxy_redirect 语法: ``` proxy_redirect [ default|off|redirect replacement ] ``` 默认值: ``` proxy_redirect default ``` 使用字段: ``` http, server, location ``` 背景:即便配置了nginx代理,当服务返回重定向报文时(http code为301或302),会将重定向的目标url地址放入http response报文的header的location字段内(该字段用于重定向)。用户浏览器收到重定向报文时,会解析出该字段并作跳转。此时新的请求报文将直接发送给真实服务地址,而非反代服务器地址。为了能让反代服务器nginx拦截此类请求,必须修改重定向报文的location信息。 假设被代理服务器返回Location字段为: `http://localhost:8000/two/some/uri/` 这个指令: ``` proxy_redirect http://localhost:8000/two/ http://frontend/one/; ``` 将Location字段重写为`http://frontend/one/some/uri/`。 如果使用“default”参数,将根据location和proxy_pass参数的设置来决定。 例如下列两个配置等效: ``` location /one/ { proxy_pass http://upstream:port/two/; proxy_redirect default; } location /one/ { proxy_pass http://upstream:port/two/; proxy_redirect http://upstream:port/two/ /one/; } ``` 在指令中可以使用一些变量: ``` proxy_redirect http://localhost:8000/ http://$host:$server_port/; ``` 参数off将在这个字段中禁止所有的proxy_redirect指令: ``` proxy_redirect off; proxy_redirect default; proxy_redirect http://localhost:8000/ /; proxy_redirect http://www.example.com/ /; ``` ## 4.5 sub_filter ngx_http_sub_module模块是一个过滤器,它修改网站响应内容中的字符串,比如你想把响应内容中的‘xxxx’全部替换成‘yyy’,这个模块已经内置在nginx中,但是默认未安装,需要安装需要加上配置参数:--with-http_sub_module 1、语法: ``` sub_filter string replacement; ``` 默认值: ``` — ``` 配置段: ``` http, server, location ``` 设置需要使用说明字符串替换说明字符串.string是要被替换的字符串,replacement是新的字符串,它里面可以带变量。 2、语法: ``` sub_filter_once on | off; ``` 默认值: ``` sub_filter_once on; ``` 配置段: ``` http, server, location ``` 字符串替换一次还是多次替换,默认替换一次,例如你要替换响应内容中的ttlsa为运维生存时间,如果有多个ttlsa出现,那么只会替换第一个,如果off,那么所有的ttlsa都会 被替换 3、语法: ``` sub_filter_types mime-type ...; ``` 默认值: ``` sub_filter_types text/html; ``` 配置段: ``` http, server, location ``` 指定需要被替换的MIME类型,默认为“text/html”,如果制定为*,那么所有的 4、示例: ``` location / { proxy_pass http://zhengde.xxx.cn; proxy_set_header Host zhengde.xxx.cn; sub_filter xxx.com yyy.com; sub_filter_once on; sub_filter_types text/html; } ``` 解释: - sub_filter 前面字符串(xxx.com)是需要替换的内容,后面字符串(yyy.com)是替换成的内容。 - sub_filter_once 意思是只查找并替换一次。on是开启此功能,off是关闭,默认值是on。 - sub_filter_types 一行意思是选定查找替换文件类型为文本型。也可以不加此行,因为默认只查找文本型文件。 # 5. 使用nginx反向代理后如何在web应用中获取用户ip 在实际应用中,我们可能需要获取用户的ip地址,比如做异地登陆的判断,或者统计ip访问次数等,通常情况下我们使用request.getRemoteAddr()就可以获取到客户端ip,但是当我们使用了nginx作为反向代理后,使用request.getRemoteAddr()获取到的就一直是nginx服务器的ip的地址。 经过反向代理后,由于在客户端和web服务器之间增加了中间层,通过`$remote_addr`变量拿到的将是反向代理服务器的ip地址,在web端使用`request.getRemoteAddr()`(本质上就是获取`$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.getAttribute("X-real-ip") **简单描述如下:** web端直接获取$remote_addr,实际上获取的是反代服务器地址,无法得到用户真实地址 但是,nginx端存在关系 ``` $remote_addr => 真实用户地址 ``` 在nginx端,新定义一个变量,将这个值存起来,发给web端 ``` $x-real-ip = $remote_addr ``` 在web端获取$x-real-ip,实际上就可以获取到真实用户地址了 --- 既然提到了真实的用户地址,这里再探讨下`X-Forwarded-For` X-Forwarded-For 是一个 HTTP 扩展头部。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP。如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中。 X-Forwarded-For 请求头格式非常简单,就这样: ``` X-Forwarded-For: client, proxy1, proxy2 ``` 可以看到,XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP。 当我们 意思是增加一个$proxy_add_x_forwarded_for到X-Forwarded-For里去,注意是增加,而不是覆盖,实际过程是如下的, 假如一个 HTTP 请求到达真实服务器(real_server)之前,经过了三个代理 Proxy1、Proxy2,IP 分别为 IP1、IP2,用户真实 IP 为 IP0,在每台代理服务器nginx增加了如下配置: ``` proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; ``` 以上配置中,$proxy_add_x_forwarded_for变量包含客户端请求头中的"X-Forwarded-For",与$remote_addr两部分,他们之间用逗号分开。以下是$proxy_add_x_forwarded_for变量在不同服务器中的值变化 ``` 那么第一台代理服务器proxy1的情形:X-Forwarded-For(空) + $remote_addr(IP0) 那么第二台代理服务器proxy2的情形:X-Forwarded-For(IP0) + $remote_addr(IP1) 那么真实服务器real_server的情形:X-Forwarded-For(IP0,IP1) + $remote_addr(IP2) ``` 此时服务端获取的X-Forwarded-For的值为 IP0,IP1,IP2,其中并没有包含真实服务器的地址,这个地址可以通过Remote Address 字段获得。 # 参考资料: https://imququ.com/post/x-forwarded-for-header-in-http.html http://tengine.taobao.org/nginx_docs/cn/docs/http/ngx_http_proxy_module.html#proxy_set_header http://gong1208.iteye.com/blog/1559835
标签:
上一篇:
nginx笔记-proxy_cache缓存详解
下一篇:
nginx笔记-负载均衡
文章分类
css
elasticsearch
git
golang
guacamole
javascript
letsencrypt
linux
nginx
other
php
python
vue
web
阅读排行
编码总结
详解网络连接
tcpdump使用
JWT
websocket协议
友情链接
node文件
laravel-vue
ArcherWong的博客园