900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > Nginx 反向代理解决跨域问题

Nginx 反向代理解决跨域问题

时间:2023-11-11 23:10:30

相关推荐

Nginx 反向代理解决跨域问题

目录

前言Nginx 反向代理常用配置Serverlocationproxy_passadd_headerOPTIONS 请求proxy_set_header跨域的 cookie 传输Proxy_cookie_domainWithCredentialsAccess-Control-Allow-CredentialsProxy_cookie_path最后

前言

编码两分钟,解决跨域两小时,我吐了。

Nginx 反向代理常用配置

打开conf/nginx.conf文件,如果只是做反向代理的话,大部分情况只需要配置http模块下的server即可,一般初始文件,只有一个server,如果你需要 Nginx 同时开启不同的端口或域名,就需要写多个server

Server

一个server模块的配置如下:

server {listen 80; # 端口号server_name localhost; # server name 默认 localhost#access_log logs/host.access.log main;location / {# 访问路径匹配规则root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html; # 错误处理location = /50x.html {root html;}}

里面比较重要的是location模块,反向代理的主要工作也是配置location

location

Location配置项定义了一条访问Nginx服务某一路径时的匹配规则,location后面紧跟的是匹配的路径,这个路径可以直接写绝对路径,可以写正则匹配:

location /api1 {# 当访问 http://localhost/api1 时命中# ...}location ~ ^/(api2/api3) {# 当访问 http://localhost/api2 和 http://localhost/api 3 时命中# ...}

proxy_pass

location里有多个配置项,其中一个是proxy_pass,意思是将当前命中的Nginx接口(例如:http://localhost/api)代理到其他 server 的接口,如下例子就是将http://localhost/api代理到/api

location /api {proxy_pass ;}

需要注意的是,在写proxy_pass不能随便在目标地址后加/,如果你在地址末尾加了/,则最终代理是这样的:

location /api {proxy_pass /; # 将会被代理到 /,后面没有 /api}

不加/,则最终代理是这样的,访问 Nginx 命中的/api,Nginx 也会自动帮你拼接上去:

location /api {proxy_pass ; # 将会代理到 /api }

add_header

location配置中的add_header选项,表示Nginx将在response中添加一些额外的响应头信息给客户端。众所周知,开启跨域支持是需要服务端配置Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers这些请求头的,那么既然有了Nginx做了中间层代理服务,就算 server 不给我们开启这些,我们完全也能够自给自足:

location /api {add_header Access-Control-Allow-Origin * always;add_header Access-Control-Allow-Headers *;add_header Access-Control-Allow-Methods "GET, POST, PUT, OPTIONS";proxy_pass ;}

其中*代表通配符,意思是所有origin都允许跨域访问,这个当然是不安全的,但是本地开发就无所谓了,你也可以写自己指定的本地服务。

一般来说,如果请求过程中出现40X50X的错误,Nginx将不会设置Access-Control-Allow-Origin继而导致跨域失败,所以需要在后面再加个always告诉 Nginx 不管怎样,都给我设置这个响应头。

OPTIONS 请求

其实,编写到这里,大多数的跨域场景,我们应该能很好的解决了,但是我的项目所需要解决的跨域问题,还不是这么简单。

首先,在开发的过程中,我发现对于有些 server ,连我发送的OPTIONS请求都过不去,查看Nginx日志才发现,Nginx本身确实帮我将OPTIONS请求代理出去了,但是服务端不认这个Request Method,它只认识POST,反手给我一个403。这就麻烦了,server 端不认这个请求,那么后续的POST根本没法发,这要咋办?

一番思考后,如果我直接让Nginx拦截OPTIONS直接响应200不就可以了嘛?

location /api {add_header Access-Control-Allow-Origin * always;add_header Access-Control-Allow-Headers *;add_header Access-Control-Allow-Methods "GET, POST, PUT, OPTIONS";if ($request_method = 'OPTIONS') {return 200;}proxy_pass ;}

重启,再试,成功。Nice,第一个问题解决。

proxy_set_header

这个配置项我的项目里并没有怎么用到,但是还是说一下,以便和add_header做出区分。

add_header是当请求从 server 端回来时,Nginx再往这个response里添加一些额外的reponse header然后发送给客户端proxy_set_header是当请求从客户端发出时,Nginx接收到request再往请求里添加一些额外的request header然后发送给服务端

常见的一些需要设置proxy_set_header的场景,比如说,有些 server 可能需要验证Host,这个时候,就可以使用proxy_set_header伪造一个Host来骗过服务端。

跨域的 cookie 传输

cookie这个东西,由于事关安全性,所以是非常敏感的。Domain不同,Path不同,浏览器就不会让你访问到这个Cookie,也不可能存储这个Cookie,更不可能让你随随便便地带着Cookie跨域名传输,所以要开启跨域情况下的Cookie传输,要求也是十分严苛的。

首先,我碰到的一个场景是:server 端需要记录访问session,以此来判定当前用户的登陆状态,后面的所有接口,都需要通过session来判断。然而当我成功调通登陆接口后,发现后续的所有接口都Error并且直接给我302到登陆界面,这就说明在服务端那里,我的状态根本就是没登陆,这是怎么回事?

一通排查,我发现是因为 server 端登陆成功后返回的一系列session我并没有做处理,所以它们的Domain都是 server 的Domain。浏览器拿到后一看,你当前域是localhost,你Cookie域是其他的域,cookie不能给你。而后面的请求没有携带这些session信息,服务端自然不认得。

一通操作,终于找到了一个叫做proxy_cookie_domain的东西。

Proxy_cookie_domain

这个选项是专门用来修改服务端返回回来的cookie domain,可以使用这个配置来将cookie domain手动修改后再返回,修改成本地开发的域,这样就能绕过浏览器的限制,成功将cookie保存。

location /api {add_header Access-Control-Allow-Origin * always;add_header Access-Control-Allow-Headers *;add_header Access-Control-Allow-Methods "GET, POST, PUT, OPTIONS";if ($request_method = 'OPTIONS') {return 200;}proxy_cookie_domain ~\.? $host;proxy_pass ;}

再试,可以,这把登陆完成后,打开控制台发现所有来自 server 的cookie都被保存下来了。接着请求其他接口,失败。

继续找原因,发现是因为就算是本地访问了Nginx服务,其实也是跨域的,只不过我将Nginx开启了支持普通跨域,可是它不支持cookie的跨域传输啊。支持跨域传输 cookie 需要前后端同时支持。

WithCredentials

首先在前端这块,我用的是axios,里面有一项安全认证的配置需要开启:withCredentials: true。将这个配置项打开,代表请求服务端进行跨域的 cookie 传输。

这个withCredentials配置项,应该是业界统一命名,命名差别不大,如果不是用的 axios 而且用的别的插件,应该都有这个配置项,写法不同而已,找一下应该能找到。

Access-Control-Allow-Credentials

搞完前端,继续搞Nginx里面需要加一个响应头信息:Access-Control-Allow-Credentials: true,代表服务端同意跨域传输凭证。

location /api {add_header Access-Control-Allow-Origin * always;add_header Access-Control-Allow-Headers *;add_header Access-Control-Allow-Methods "GET, POST, PUT, OPTIONS";add_header Access-Control-Allow-Credentials true;if ($request_method = 'OPTIONS') {return 200;}proxy_cookie_domain ~\.? $host;proxy_pass ;}

再试,失败,我*****

再找原因,发现如果开启了Access-Control-Allow-Credentials那么Access-Control-Allow-OriginAccess-Control-Allow-Headers不能写成*这种通配符的形式,必须写明 origin 和 headers。

location /api {add_header Access-Control-Allow-Origin http://localhost:3000 always;add_header Access-Control-Allow-Headers "Accept,Accept-Encoding,Accept-Language,Connection,Content-Length,Content-Type,Host,Origin,Referer,User-Agent";add_header Access-Control-Allow-Methods "GET, POST, PUT, OPTIONS";add_header Access-Control-Allow-Credentials true;if ($request_method = 'OPTIONS') {return 200;}proxy_cookie_domain ~\.? $host;proxy_pass ;}

重启Nginx,再试,成功了。

Proxy_cookie_path

在解决问题的过程中,我发现还有个叫做proxy_cookie_path的配置,虽然我没用到,但还是记录一下,这个配置的作用看名字就知道,应该和proxy_cookie_domain类似。

一般情况下,server 返回的 cookie 里,可能会写有Path,如果浏览器的当前Path不满足这个cookie path的限定条件,cookie 同样是不可操作的。这个proxy_cookie_path配置就是为了将 server 返回的 cookie path 改写成自己需要的 cookie path 从而绕过浏览器的限制。

最后

给个 Nginx 配置项官方传送:Nginx 配置

和一篇不错的 nginx 的博客:Nginx 使用与异常处理

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。