利用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配置ssl实现https的方法示例
Mar 31 Servers
Nginx设置日志打印post请求参数的方法
Mar 31 Servers
Nginx域名转发使用场景代码实例
Mar 31 Servers
NGINX 权限控制文件预览和下载的实现原理
Jan 18 Servers
CentOS下安装Jenkins的完整步骤
Apr 07 Servers
CentOS安装Nginx并部署vue
Apr 12 Servers
Windows Server 2019 域控制器安装图文教程
Apr 28 Servers
KVM基础命令详解
Apr 30 Servers
利用nginx搭建RTMP视频点播、直播、HLS服务器
May 25 Servers
Windows10安装Apache2.4的方法步骤
Jun 25 Servers
Zabbix6通过ODBC方式监控Oracle 19C的详细过程
Sep 23 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
详细介绍:Apache+PHP+MySQL配置攻略
2006/09/05 PHP
php中定义网站根目录的常用方法
2010/08/08 PHP
编译php 5.2.14+fpm+memcached(具体操作详解)
2013/06/18 PHP
PHP实现支付宝即时到账功能
2016/12/21 PHP
文本框获得焦点和失去焦点的判断代码
2012/03/18 Javascript
js/jQuery对象互转(快速操作dom元素)
2013/02/04 Javascript
JS实现距离上次刷新已过多少秒示例
2014/05/23 Javascript
jquery+php随机生成红包金额数量代码分享
2015/08/27 Javascript
浅谈javascript 函数表达式和函数声明的区别
2016/01/05 Javascript
JS实现鼠标框选效果完整实例
2016/06/20 Javascript
基于JavaScript实现轮播图代码
2016/07/14 Javascript
AngularJS基础 ng-mouseleave 指令详解
2016/08/02 Javascript
关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)
2016/12/14 Javascript
详谈Ajax请求中的async:false/true的作用(ajax 在外部调用问题)
2017/02/10 Javascript
Vue自定义指令拖拽功能示例
2017/02/17 Javascript
纯JS单页面赛车游戏制作代码分享
2017/03/03 Javascript
浅谈struts1 & jquery form 文件异步上传
2017/05/25 jQuery
JS实现仿UC浏览器前进后退效果的实例代码
2017/07/17 Javascript
vue 自定义指令自动获取文本框焦点的方法
2018/08/25 Javascript
vue动态设置img的src路径实例
2018/09/18 Javascript
express启用https使用小记
2019/05/21 Javascript
vue中使用WX-JSSDK的两种方法(推荐)
2020/01/18 Javascript
python实现在控制台输入密码不显示的方法
2015/07/02 Python
Python django框架输入汉字,数字,字符生成二维码实现详解
2019/09/24 Python
pytorch中的自定义数据处理详解
2020/01/06 Python
Python中logger日志模块详解
2020/08/04 Python
python 实现汉诺塔游戏
2020/11/28 Python
新秀丽拉杆箱美国官方网站:Samsonite美国
2016/07/25 全球购物
美国办公用品折扣网站:Shoplet.com
2019/11/24 全球购物
经理秘书找工作求职信
2013/12/19 职场文书
销售总监岗位职责
2014/01/04 职场文书
市场部管理制度
2014/02/02 职场文书
中学生演讲稿
2014/04/26 职场文书
财务部岗位职责
2015/02/03 职场文书
军训新闻稿范文
2015/07/17 职场文书
2016幼儿园毕业感言
2015/12/08 职场文书