利用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 相关文章推荐
Centos7.7 64位利用本地完整安装包安装lnmp/lamp套件教程
Mar 09 Servers
Nginx服务器添加Systemd自定义服务过程解析
Mar 31 Servers
Nginx代理同域名前后端分离项目的完整步骤
Mar 31 Servers
nginx 防盗链防爬虫配置详解
Mar 31 Servers
图文详解nginx日志切割的实现
Jan 18 Servers
Windows Server 2019 安装DHCP服务及相关配置
Apr 28 Servers
tomcat下部署jenkins的方法
May 06 Servers
Windows Server 2022 超融合部署(图文教程)
Jun 25 Servers
TaiShan 200服务器安装Ubuntu 18.04的图文教程
Jun 28 Servers
Apache Kafka 分区重分配的实现原理解析
Jul 15 Servers
win10搭建配置ftp服务器的方法
Aug 05 Servers
Nginx跨域问题解析与解决
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代码
2010/08/08 PHP
PHP几个实用自定义函数小结
2016/01/25 PHP
PHP 面向对象程序设计之类属性与类常量实现方法分析
2020/04/13 PHP
Bookmarklet实现启动jQuery(模仿 云输入法)
2010/09/15 Javascript
JS实现在Repeater控件中创建可隐藏区域的代码
2010/09/16 Javascript
关于IE浏览器以及Firefox下的javascript冒泡事件的响应层级
2010/10/14 Javascript
javascript中parentNode,childNodes,children的应用详解
2013/12/17 Javascript
对于Form表单reset方法的新认识
2014/03/05 Javascript
使用jquery解析XML示例代码
2014/09/05 Javascript
举例说明如何为JavaScript的方法参数设置默认值
2015/11/17 Javascript
Bootstrap实现响应式导航栏效果
2015/12/28 Javascript
JQuery 进入页面默认给已赋值的复选框打钩
2017/03/23 jQuery
使用gulp搭建本地服务器并实现模拟ajax
2017/04/05 Javascript
微信小程序商城项目之购物数量加减(3)
2017/04/17 Javascript
jQuery序列化后的表单值转换成Json
2017/06/16 jQuery
完美解决axios跨域请求出错的问题
2018/02/05 Javascript
js自定义trim函数实现删除两端空格功能
2018/02/09 Javascript
node.js博客项目开发手记
2018/03/16 Javascript
vue 本地服务不能被外部IP访问的完美解决方法
2018/10/29 Javascript
tsconfig.json配置详解
2019/05/17 Javascript
koa router 多文件引入的方法示例
2019/05/22 Javascript
vue实现行列转换的一种方法
2019/08/06 Javascript
解决Vue动态加载本地图片问题
2019/10/09 Javascript
vue实现桌面向网页拖动文件的示例代码(可显示图片/音频/视频)
2021/03/01 Vue.js
17个Python小技巧分享
2015/01/23 Python
Python多线程编程(六):可重入锁RLock
2015/04/05 Python
CentOS安装pillow报错的解决方法
2016/01/27 Python
python3实现指定目录下文件sha256及文件大小统计
2019/02/25 Python
快速解决docker-py api版本不兼容的问题
2019/08/30 Python
Python如何访问字符串中的值
2020/02/09 Python
瑞典首都斯德哥尔摩的多元奢侈时尚品牌:Acne Studios
2017/07/09 全球购物
Nisbets爱尔兰:英国最大的厨房和餐饮设备供应商
2019/01/26 全球购物
公司年会演讲稿范文
2014/01/11 职场文书
《大海那边》教学反思
2014/04/09 职场文书
让世界充满爱演讲稿
2014/05/24 职场文书
卫生院艾滋病宣传活动总结
2015/05/09 职场文书