利用Nginx代理如何解决前端跨域问题详析


Posted in Servers onApril 02, 2021

前言

Nginx(发音同“engine X”)是异步框架的网页服务器,也可以用作反向代理、负载平衡器和HTTP缓存。

本文将讲述如何使用 Nginx 在 Web 前后端分离开发中实现路由的转发。

Web 开发通常使用的是前后端分离的开发模式,即前端和后端分别进行开发,前端通过 Ajax 请求后端的接口,将获取数据将数据渲染到页面上。前端开发会使用脚手架搭建前端开发环境,其底层通常会启动一个本地服务器,通常使用的是 nodejs 的 Express 框架。而后端则是提供接口,一般是放在线上的一个开发用的域名下。

这在开发过程中会导致 跨域 问题,即在一个域名下的网页,是无法通过 Ajax 请求另一个(不同源)域名下的接口 API 的。这是浏览器的同源策略,是浏览器的一个非常重要的安全策略。

解决这个问题的其中一个方案是使用 代理。具体来说,就是在本地启动一个服务器(如 localhost:4000),发送给该服务器的请求会根据请求路由(比如判断 url 是否有前缀 /api)进行转发,分别转发到前端开发的服务器(如 localhost:3000),以及后端服务器(比如 dev.yoursite.com)。这样通过一个代理服务器,因为请求的 api 都是同一个域名下的,自然就不会造成跨域问题,从而导致请求失败。

下面我们就来讲解如何使用 Nginx 来实现反向代理。

简单认识 Nginx 配置文件

安装好 Nginx 后,我们需要确定下 Nginx 的默认配置文件的位置。执行命令 nginx -t,该命令会检测 nginx 的默认配置文件语法是否正确,并进行测试,最后输出结果。我们可以从输出中得到默认配置文件所在的位置。

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

还有另外一种方法可以得到默认配置文件的位置,那就是执行 nginx -h。该命令会输出 nginx 的简易帮助文档,其中的 -c filename 的配置项说明也指出了默认配置项的路径。

-c filename : set configuration file (default: /etc/nginx/nginx.conf)

通过这个文档,我们也可以知道,使用 -c 配置项可以自定义配置文件。如果不指定文件,使用默认配置文件。

下面我们来修改一下 Nginx 到这个默认配置文件 nginx.config 来首先代理功能。

nginx.config 文件的 http 后面的代码块中,应该会有类似下面这行的代码:

include /etc/nginx/conf.d/*.conf;

这行代码的作用是将 /etc/nginx/conf.d 目录下的后缀为 .conf 的文件内容嵌入到引入位置中,作为配置的一部分执行。

如果你是在 macOS 安装的 Nginx,可能会有点不同。我使用 brew 安装的 Nginx 为为 include servers/*;,对应嵌入的是 servers 目录下的所有文件。

为什么会用到这种嵌入的语法呢?因为这样我们就可以将不同项目需要用到的配置放到不同的配置文件里,好处就是可以快速地找到对应项目要修改的配置文件,不用担心不小心修改了其他项目的配置。另外如果直接在 nginx.conf 上修改,使其变得臃肿。这符合设计模式的 单一职责原则。

此外,你可以会奇怪conf.d 目录的命名为什么要加上 .d ?如果你使用 Linux 过一段时间,你会发现某些目录或文件的末尾会加上一个 d,比如 httpd、crond、vsftpd 等。其实这是为了说明这些文件都属于是 daemon(服务)。这里的服务指的是系统的服务,主要分为系统本身需要的服务,以及负责网络的服务。我们的 conf.d 就是属于后者。

编写 Nginx 配置文件

我们在 conf.d 目录下创建名为 demo.conf 的文件,写入以下内容,然后启动 Nginx。

server {
 listen 5000;
 server_name localhost;

 location / {
 proxy_pass http://localhost:3000;
 }
 location /api/ {
 proxy_pass http://localhost:4000;
 }
}

该配置启用了 localhost:5000 的服务器,将 localhost:5000 下开头为 /api/ url 请求代理到了 localhost:4000(后端接口服务器)。其他请求则是代理到 localhost:3000(前端)。下面我们具体解析一下配置文件里面内容的作用。

listen 设置了服务器的端口号,server_name 则设置了主机名。

location

location 表示进行路由的匹配,如果匹配则执行对应代码块里的操作。location 可以使用 前缀匹配 以及 正则匹配(需要以 ~* 或 ~ 开头)。我们这里的配置使用的是前缀匹配。

这里有个点需要注意一下,Nginx 的路由匹配和一般的按顺序匹配第一个的路由匹配方案(比如后端的 gin、前端的 vue-router 的路由匹配方案)不同,nginx 匹配路由的方式为:

  1. 首先进行前缀匹配,遍历所有的前缀匹配,从中选择前缀匹配最长的;
  2. 然后会进行正则匹配,在所有正则匹配中,从前往后选择第一个符合的;
  3. 如果能找到匹配的正则匹配,使用其对应的配置;如果没有,则使用之前找到的那个最长的前缀匹配对应的配置。

所以,当请求为 localhost:5000/api/xx 时, / 和 /api/ 都能够前缀匹配。根据规则,虽然位置更靠前的 / 也符合前缀匹配,但 /api 更长,所以最终匹配的是 /api。

proxy_pass

确定好匹配的 location 后,我们再看看 proxy_pass 又做了什么操作。proxy_pass 用于将请求路由映射到指定的协议和地址。本质是将发送给 Nginx 的请求处理并发送到另一个服务器,然后将返回的数据作为 Nginx的返回数据返回。

proxy_pass 后如果使用的是 URI(端口后面至少有一个 /),那么 Nginx 就会 替换 掉 location 匹配的那部分字符。

listen 5000;
server_name localhost;
location /name/ {
 proxy_pass http://127.0.0.1/remote/; 
}
# localhost:5000/name/fstar
# 会被映射请求为
# 127.0.0.1/remote/fstar

可以看到,/name/ 的部分在映射时被移除(或者说是替换)了。

proxy_pass 后如果使用的是不是 URI(端口后没有任何东西),Nginx 会将源请求完全映射到代理服务上:

listen 5000;
server_name localhost;
location /some/path/ {
 proxy_pass http://127.0.0.1;
}

# localhost:5000/some/path/x/y
# 会被映射请求为
# 127.0.0.1/some/path/x/y

这里的 /some/path 并没有被移除。

我们的 demo.conf 文件的 proxy_pass 使用的不是 URI,所以是将路由完全映射到另一个服务。

思考题

请问,下面有两段配置(区别是 proxy_pass 结尾是否有 /)?如果请求 /kite/api/xx,分别会映射为什么?

location /kite/api/ {
 proxy_pass http://localhost:5000;
}
location /kite/api/ {
 proxy_pass http://localhost:5000/;
}

前面我们讲 proxy_pass 的时候说过,proxy_pass 后面如果不是 URI,会正常转发;如果是 URI,就移除 location 匹配的前缀再进行转发,体现的是替换路由的效果。上面这两个配置的区别就在于末尾的这个 /,有 / 是 URI,没有的不是 URI,从而导致完全不一样的结果,依次分别为:

http://localhost:5000/kite/api/xx
http://localhost:5000/xx

所以,在写 Nginx 配置的时候,一定要注意端口后面的 / 是否有必要保留。因为它的有无会导致两种截然不同的效果。

参考文章

总结

到此这篇关于利用Nginx代理如何解决前端跨域问题的文章就介绍到这了,更多相关Nginx代理解决前端跨域内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Servers 相关文章推荐
Nginx实现高可用集群构建(Keepalived+Haproxy+Nginx)
May 27 Servers
制作能在nginx和IIS中使用的ssl证书
Jun 21 Servers
Nginx反向代理配置的全过程记录
Jun 22 Servers
Linux安装apache服务器的配置过程
Nov 27 Servers
Nginx工作模式及代理配置的使用细节
Mar 21 Servers
Vertica集成Apache Hudi重磅使用指南
Mar 31 Servers
Windows Server 2008 修改远程登录端口以及配置防火墙
Apr 28 Servers
配置nginx负载均衡
May 06 Servers
使用Apache Camel表达REST服务的方法
Jun 10 Servers
vscode远程免密登入Linux服务器的配置方法
Jun 28 Servers
nginx之内存池的实现
Jun 28 Servers
Linux在两个服务器直接传文件的操作方法
Aug 05 Servers
Nginx URL重写rewrite机制原理及使用实例
Apr 01 #Servers
nginx限制并发连接请求数的方法
Apr 01 #Servers
Nginx已编译的nginx-添加新模块
Nginx下配置Https证书详细过程
详解Nginx启动失败的几种错误处理
Apr 01 #Servers
Nginx 根据URL带的参数转发的实现
Apr 01 #Servers
Nginx Rewrite使用场景及配置方法解析
You might like
php线性表顺序存储实现代码(增删查改)
2012/02/16 PHP
Laravel框架查询构造器 CURD操作示例
2019/09/04 PHP
jquery 简短右键菜单 多浏览器兼容
2010/01/01 Javascript
JS下高效拼装字符串的几种方法比较与测试代码
2010/04/15 Javascript
举例讲解JavaScript中关于对象操作的相关知识
2015/11/16 Javascript
JavaScript数组去重的两种方法推荐
2016/04/05 Javascript
怎么引入(调用)一个JS文件
2016/05/26 Javascript
JS基础随笔(菜鸟必看篇)
2016/07/13 Javascript
Bootstrap表单布局
2016/07/19 Javascript
详解nodejs 文本操作模块-fs模块(二)
2016/12/22 NodeJs
Nodejs+Socket.io实现通讯实例代码
2017/02/13 NodeJs
vue2.0+vue-dplayer实现hls播放的示例
2018/03/02 Javascript
Angular5中提取公共组件之radio list的实例代码
2018/07/10 Javascript
vue-cli配置flexible过程详解
2019/07/04 Javascript
通过vue写一个瀑布流插件代码实例
2019/09/07 Javascript
如何构建 vue-ssr 项目的方法步骤
2020/08/04 Javascript
JS实现纸牌发牌动画
2021/01/19 Javascript
[51:36]Optic vs Newbee 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
[40:27]完美世界DOTA2联赛PWL S3 PXG vs GXR 第一场 12.19
2020/12/24 DOTA
python函数局部变量用法实例分析
2015/08/04 Python
Python 内置函数memoryview(obj)的具体用法
2017/11/23 Python
神经网络python源码分享
2017/12/15 Python
分析python请求数据
2018/08/19 Python
Django使用AJAX调用自己写的API接口的方法
2019/03/06 Python
一篇文章了解Python中常见的序列化操作
2019/06/20 Python
在python plt图表中文字大小调节的方法
2019/07/08 Python
Python完成哈夫曼树编码过程及原理详解
2019/07/29 Python
keras自定义损失函数并且模型加载的写法介绍
2020/06/15 Python
简单了解Django项目应用创建过程
2020/07/06 Python
python使用建议与技巧分享(二)
2020/08/17 Python
大学团支书的自我评价分享
2013/12/14 职场文书
教师学习培训邀请函
2014/02/04 职场文书
老公保证书范文
2014/04/29 职场文书
北京天坛导游词
2015/02/12 职场文书
银行求职信模板
2015/03/20 职场文书
2015年父亲节寄语
2015/03/23 职场文书