分类: Linux

Nginx跨域配置


作者:朱培鑫

时间:2021-07-01

Nginx 配置如下(server部分、location部分任选其一)

  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  add_header Access-Control-Allow-Origin "$http_origin";
  add_header Access-Control-Allow-Credentials true;
  add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
  add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
  if ($request_method = 'OPTIONS') {
      return 204;
  }

如何理解反向代理?

Nginx是反向代理服务器,我们可以从下面的图来理解为什么是反向代理。以Nginx为中心,数据的流向是从Server到Nginx再到Client,注意这里说的是数据(响应数据),而不是请求。我们都知道水流一定是从上游流到下游,所以给Server一个别称上游服务器,当然这个别称并不是我定义的。
20190709170853778.png

proxy_set_header和add_header的区别

区别: proxy_set_header是Nginx设置请求头信息给上游服务器,add_header是Nginx设置响应头信息给浏览器。

proxy_set_header

假如Nginx请求上游服务器时,添加额外的请求头,就需要使用proxy_set_header。在Java中使用HttpServletRequest#getHeader(String name)来获取请求头的值,name是请求头的名称。
例如:proxy_set_header X-Request-URI $scheme://$host/$uri;

  String requestUrl = request.getHeader("X-Request-URI");
  if (requestUrl == null) {
    // 从Servlet服务器获取客户端请求地址
    requestUrl = request.getRequestURL().toString();
  }

add_header

Nginx响应数据时,要告诉浏览器一些头信息,就要使用add_header。例如跨域访问

add_header 'Access-Control-Allow-Origin' '*';

Nginx中的Host、X-Real-IP、X-Forwarded-For

客户端地址(请求服务的地址):192.168.1.1
Nignx服务器地址:192.168.1.2
后端服务器地址:192.168.1.3

X-Real-IP

是指客户端的真实IP,如果设置了$remote_addr这个值,后端服务器就能获取到客户端的真实IP,也就是此例中的192.168.1.1

Host

host的值设置为$proxy_host,是指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。
$http_host不是一个固定的变量,他其实是$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。

跨域头设置

Access-Control-Allow-Origin

响应头指定了该响应的资源是否被允许与给定的origin共享。
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Origin

Access-Control-Allow-Credentials

响应头表示是否可以将对请求的响应暴露给页面。返回true则可以,其他值均不可以。 Credentials可以是 cookies, authorization headers 或 TLS client certificates。
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials

Access-Control-Allow-Methods

在对 preflight request.(预检请求)的应答中明确了客户端所要访问的资源允许使用的方法或方法列表。
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Methods

Access-Control-Allow-Headers

用于 preflight request (预检请求)中,列出了将会在正式请求的 Access-Control-Request-Headers 字段中出现的首部信息。
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Headers

OPTIONS请求

HTTP 的 OPTIONS 方法 用于获取目的资源所支持的通信选项。在CORS中,当HTTP请求为复杂请求时,会发起OPTIONS预检请求。
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/OPTIONS

简单请求和复杂请求

简单请求

满足以下条件的请求即为简单请求:请求方法:GET、POST、HEAD
除了以下的请求头字段之外,没有自定义的请求头

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width
  • Content-Type 的值只有以下三种(Content-Type一般是指在post请求中,get请求中设置没有实际意义) text/plain、multipart/form-data、application/x-www-form-urlencoded

请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器 (未验证)
XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问
请求中没有使用 ReadableStream 对象 (未验证)

复杂请求

非简单请求即为复杂请求。复杂请求我们也可以称之为在实际进行请求之前,需要发起预检请求的请求。

附带身份凭证的请求

一般而言,对于跨域 XMLHttpRequest或 Fetch 请求,浏览器不会发送身份凭证信息。如果要发送凭证信息,需要设置 XMLHttpRequest 的某个特殊标志位。

如果在发送请求的时候,给xhr 设置了withCredentials为true,从而向服务器发送 Cookies,如果服务端需要想客户端也发送cookie的情况,需要服务器端也返回Access-Control-Allow-Credentials: true响应头信息。
对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin的值为“*”。
这是因为请求的首部中携带了Cookie信息,如果 Access-Control-Allow-Origin的值为“*”,请求将会失败。而将 Access-Control-Allow-Origin的值设置为 http://foo.example(请求源),则请求将成功执行。

( 本篇完 )