本文转自:https://www.jianshu.com/p/15f3498a7fad

X-Forwarded-For和相关几个头部的理解

$remote_addr
    是nginx与客户端进行TCP连接过程中,获得的客户端真实地址. Remote Address 无法伪造,因为建立 TCP 连接需要三次握手,如果伪造了源 IP,无法建立 TCP 连接,更不会有后面的 HTTP 请求

X-Real-IP
    是一个自定义头。X-Real-Ip 通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端。需要注意的是,X-Real-Ip 目前并不属于任何标准,代理和 Web 应用之间可以约定用任何自定义头来传递这个信息

X-Forwarded-For
    X-Forwarded-For 是一个扩展头。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。

如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:

X-Forwarded-For: IP0, IP1, IP2

Proxy3 直连服务器,它会给 XFF 追加 IP2,表示它是在帮 Proxy2 转发请求。列表中并没有 IP3,IP3 可以在服务端通过 $remote_address 字段获得。我们知道 HTTP 连接基于 TCP 连接,HTTP 协议中没有 IP 的概念,$remote_address 来自 TCP 连接,表示与服务端建立 TCP 连接的设备 IP,在这个例子里就是 IP3。

详细分析一下,这样的结果是经过这样的流程而形成的:

用户IP0---> 代理Proxy1(IP1),Proxy1记录用户IP0,并将请求转发个Proxy2时,带上一个Http Header
    X-Forwarded-For: IP0
    Proxy2收到请求后读取到请求有 X-Forwarded-For: IP0,然后proxy2 继续把链接上来的proxy1 ip追加到 X-Forwarded-For 上面,构造出X-Forwarded-For: IP0, IP1,继续转发请求给Proxy 3
    同理,Proxy3 按照第二部构造出 X-Forwarded-For: IP0, IP1, IP2,转发给真正的服务器,比如NGINX,nginx收到了http请求,里面就是 X-Forwarded-For: IP0, IP1, IP2 这样的结果。所以Proxy 3 的IP3,不会出现在这里。
    nginx 获取proxy3的IP 能通过$remote_address获取到,因为这个$remote_address就是真正建立TCP链接的IP,这个不能伪造,是直接产生链接的IP。$remote_address 无法伪造,因为建立 TCP 连接需要三次握手,如果伪造了源 IP,无法建立 TCP 连接,更不会有后面的 HTTP 请求。

x-forwarded-for 实践研究:

uwsgi_pass的情况下,nginx 没有设置proxy_pass x-forwarded-for: $proxy_add_x_forwarded_for;
    如果请求头传了XFF,在flask里面能正常读取请求头里面的XFF,就是当是一个普通的头读出;如果header不传这个XFF的话,就读不到

proxy_pass 情况下

没有传 # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for 的话,跟上面的uwsgi_pass 一样,都是在没有设置header XFF情况下,读不到。

如果传了 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for, header 不传xff 的话,也是可以在程序里面读到Xff 头: X-Forwarded-For: 10.0.2.2 (这个IP就是真正连上nginx 的IP, 也就是$remote_address),因为这句proxy_set_header 会让nginx追加一个$remote_address到XFF。

header 传xff的话, 程序里面可以读到Xff 头: X-Forwarded-For: 188.103.19.120, 10.0.2.2 (第一个是我自己编的,第二个是$remote_address),nginx还是会因为proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for 这句而追加$remote_addr到XFF。

总结:

只要nginx前端(例如lvs, varnish)转发请求给nginx的时候,带了x-forwarded-for ,那么程序就一定能读到这个字段,如果nginx还设置了proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for, 那么程序能读到XFF是:ip0, ip1 (客户端Ip,lvs或者varnishIP)。 如果nginx没有设置,那么nginx还是会原样把http头传给程序,也就是说程序也能读到XFF,而且XFF就是ip0 客户端IP。

proxy_pass 设置这个头 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 是站在一个作为代理的角度把。能继续传输多级代理的头。

nginx的日志格式写了$http_x_forwared_for 说明前端(lvs)确实传了这个头过来。所以是程序是读取到的

uwsgi_pass 不能设置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 这个头,是因为这个头是对http代理来说,用来传递IP的,uwsgi 不可能充当一个代理。

nginx->程序,这里其实有两个链接过程,其他IP与nginx的TCP链接, nginx与程序的TCP链接。所以$remote_addr都是对各自来说的。
    程序的remote_addr: remote_addr 127.0.0.1 (跟它链接的是nginx 内网127.0.0.1)
    nginx的remote_addr : X-Real-Ip: 10.0.2.2 (跟它链接的是我的电脑,IP 10.0.2.2)

对程序来说,读取的request.remote_addr 也永远是直接跟他链接的ip, 也就是反向代理nginx

The access_route attribute uses the X-Forwarded-For header, falling back to the REMOTE_ADDRWSGI variable; 也就是说access_route默认读取XFF头,如果没有,降级读取WSGI的REMOTE_ADDR变量,这个 WSGI的REMOTE_ADDR变量 就是 $remote_addr

request.envron 是WSGI的变量,都是wsgi server转过来的,普通的头都是加了HTTP_前缀的 ,包括proxy_set_header Host $host:8000;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    添加的头都会出现在处理,因为他们就是普通的http头

LVS->nginx的情况下, 请求的时候主动加XFF,程序读取的时候没显示。因为LVS设置XFF的时候,直接把直连的IP赋值给LVS,忽略掉所有本来有的XFF,要从LVS这里开始。 所以程序读到的XFF是 :XFF headers 218.107.55.254, 10.120.214.252
    前面的是我的IP, 后面的是LVS的IP

{
  "wsgi.multiprocess": "False",
  "SERVER_SOFTWARE": "Werkzeug/0.11.10",
  "SCRIPT_NAME": "",
  "REQUEST_METHOD": "GET",
  "PATH_INFO": "/api/get_agreement_url/",
  "SERVER_PROTOCOL": "HTTP/1.0",
  "QUERY_STRING": "",
  "werkzeug.server.shutdown": "<function shutdown_server at 0x7f4a2f4e5488>",
  "CONTENT_LENGTH": "",
  "SERVER_NAME": "127.0.0.1",
  "REMOTE_PORT": 58284,
  "werkzeug.request": "",
  "wsgi.url_scheme": "http",
  "SERVER_PORT": "6000",
  "HTTP_POSTMAN_TOKEN": "666cfd97-585b-c342-f0bd-5c785dfff27d",
  "wsgi.input": "",
  "wsgi.multithread": "False",
  "HTTP_CACHE_CONTROL": "no-cache",
  "HTTP_ACCEPT": "*/*",
  "wsgi.version": "(1, 0)",
  "wsgi.run_once": "False",
  "wsgi.errors": "",
  "CONTENT_TYPE": "",
  "REMOTE_ADDR": "127.0.0.1",

"HTTP_CONNECTION": "close",
  "HTTP_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
  "HTTP_ACCEPT_LANGUAGE": "zh-CN,zh;q=0.8,en;q=0.6",
  "HTTP_X_FORWARDED_FOR": "10.0.2.2",
  "HTTP_ACCEPT_ENCODING": "gzip, deflate, sdch",
  "HTTP_HOST": "[test.mumu.nie.netease.com:8000](http://test.mumu.nie.netease.com:8000/)",

}

$proxy_add_x_forwarded_for; nginx的这个变量含义就是,每次都追加$remote_address 到 xff头,如果xff头不存在,那么xff就被设置成跟$remote_address 一样了。如果本来就存在,就追加了 ip1, ip2这样的形式

转载:HTTP 请求头中的 X-Forwarded-For的更多相关文章

  1. HTTP 请求头中的 X-Forwarded-For(转)

    原文:https://imququ.com/post/x-forwarded-for-header-in-http.html 我一直认为,对于从事 Web 前端开发的同学来说,HTTP 协议以及其他常 ...

  2. Http 请求头中的 Proxy-Connection

    平时用 Chrome 开发者工具抓包时,经常会见到 Proxy-Connection 这个请求头.之前一直没去了解什么情况下会产生它,也没去了解它有什么含义.最近看完<HTTP 权威指南> ...

  3. js 中ajax请求时设置 http请求头中的x-requestd-with= ajax

    今天发现 AngularJS 框架的$http服务提供的$http.get() /$http.post()的ajax请求中没有带 x-requested-with字段. 这样的话,后端的php 就无法 ...

  4. HTTP 请求头中的 Remote_Addr,X-Forwarded-For,X-Real-IP

    REMOTE_ADDR 表示发出请求的远程主机的 IP 地址,remote_addr代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的ip指定的,当你的浏览器访问某个网站时,假设中间 ...

  5. shiro + jwt 实现 请求头中的 rememberMe 时间限制功能

    前言: 上一篇提出, 通过修改 rememberMe 的编码来实现 rememberMe的功能的设想, 事后我去尝试实现了一番, 发现太麻烦, 还是不要那么做吧. 程序还是要越简单越好. 那功能总是要 ...

  6. shiro 获取请求头中的 rememberMe

    前言: 上一篇提到了, 将 sessionId 放到请求头中去, 那rememberMe是否也可以放到请求头中去呢. 其实不管是sessionId还是rememberMe, shiro都会默认往coo ...

  7. shiro 获取请求头中的 sessionId

    前言: 在前后端项目中, 前端有可能会要求, 后台返回一个 sessionId 给他, 然后他在请求后台接口时, 把这个sessionId 带给后台, 后台拿到这个sessionId , 就能识别, ...

  8. Ajax 请求头中常见content-type

    四种常见的 POST 提交数据方式 HTTP 协议是以 ASCII 码传输,建立在 TCP/IP 协议之上的应用层规范.规范把 HTTP 请求分为三个部分:状态行.请求头.消息主体.协议规定 POST ...

  9. WebAPi获取请求头中对应键值

    /// <summary> /// 依据键获取请求头中值数据 /// </summary> /// <param name="request"> ...

随机推荐

  1. lambda表达式和for_each,find_if

    1 lambda表达式可以允许我传递任意可调用对象,必须要有捕获列表和函数体,标准形式是[捕获列表] (参数列表)->return tpye{函数体} 谓词:一元谓词指的是只能接受一个传入参数, ...

  2. 088、Java中String类之对象直接赋值

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...

  3. Matplotlib 基本概念

    章节 Matplotlib 安装 Matplotlib 入门 Matplotlib 基本概念 Matplotlib 图形绘制 Matplotlib 多个图形 Matplotlib 其他类型图形 Mat ...

  4. SciPy 积分

    章节 SciPy 介绍 SciPy 安装 SciPy 基础功能 SciPy 特殊函数 SciPy k均值聚类 SciPy 常量 SciPy fftpack(傅里叶变换) SciPy 积分 SciPy ...

  5. springboot内嵌定时任务使用及cron表达式讲解

    第一步:pom引入依赖 <dependencies> <dependency> <groupId>org.springframework.boot</grou ...

  6. C语言三种整数类型

    1,int 是 C 语言的基本整数类型,可以满足我们处理一般数据的需求. C 语言还提供了四个可以修饰 int 的关键字:short.long.signed,以及 unsigned. 利用这四个关键字 ...

  7. maven安装和eclipse集成遇到的问题

    修改完maven的位置之后,修改配置文件conf/settings.xml <localRepository>E:/apache-maven-3.3.1-bin/mvn/mvnreposi ...

  8. Java从.CSV文件中读取数据和写入

    .CSV文件是以逗号分割的数据仓储,读取数据时从每一行中读取一条数据元祖,也就是一条数据,再用字符分割的方式获取表中的每一个数据项. import java.io.BufferedReader;    ...

  9. HTML<figure> <figcaption> 标签定义图文并茂

    本来想分两篇文章来解释说明figure.figcaption的,但是这俩个标签都是定义图文的,所以我们合起来讲解,大家更能容易接受. 大家在写xhtml.html中常常用到一种图片列表,图片+标题 或 ...

  10. nginx配置多个项目

    使用Nginx要在同一个域名下配置多个项目有两种方式: nginx按不同的目录分发给不同的项目 启用二级域名,不同的项目分配不同的二级域名 1.nginx按不同的目录分发给不同的项目: server ...