Skip to main content

使用nginx解决常见的跨域问题

· 约6分钟
真的猴赛雷

引言

前置知识:跨源资源共享(CORS)

在本地进行前后端开发,经常会遇见跨域问题,对于新手来说可能比较头疼。

cors-error

本文主要讨论,如何使用nginx反向代理,来解决跨域问题。


代码准备

gitlab仓库地址:https://lab.co.link/study/cors-demo

或参照以下内容,创建代码文件。

前端网页代码

创建index.html,复制以下内容。

<html>

<head>
<title>CORS Demo</title>
<script>
const SERVER_ADDR = "http://127.0.0.1:8000/api/health";

function request() {
fetch(SERVER_ADDR).then((res) => {
console.log(res);
});
}
</script>
</head>

<body>
<button onclick="request();">Request</button>
</body>

</html>

后端服务代码

创建server.js,复制以下内容。

const http = require('http');

const server = http.createServer();

server.on('request', function (request, response) {
console.log('get request: ', request.method, request.url);
response.end("ok");
});

server.listen(8000);
console.log("server running...");

nginx配置文件

创建nginx.conf,复制以下内容。

worker_processes  auto;

error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;


server {
listen 8080;

# backend-addr
location /api/ {
proxy_pass http://host.docker.internal:8000;
}

# frontend-addr
location / {
proxy_pass http://host.docker.internal:5500;
}
}
}

问题场景重现

启动前端服务

为便于演示,使用vscode的Live Server扩展,启动前端网页服务,服务地址是http://127.0.0.1:5500 (根据个人vscode本地配置,地址可能有所不同)。

也可使用其他自己偏好的方式启动前端网页服务。

前端服务地址不一致的,需自行更新本文涉及到的前端服务地址。

run-frontend

启动后端服务

执行node server.js,启动后端服务,服务地址是http://127.0.0.1:8000。

问题重现

打开浏览器控制台,然后点击Request按钮,发现控制台出现跨域报错。

cors-error

原因是运行在http://127.0.0.1:5500 的JS代码,发起了到http://127.0.0.1:8000 的请求,两者属于不同域,是跨域请求;而后端服务器又没有配置允许跨域。


解决方案

  1. 在后端服务器配置允许跨域,如nodejs经常会使用到的cors依赖。

  2. 使用nginx进行反向代理,使前端服务与后端服务处于同一域。

鉴于大家使用的语言及web框架都比较丰富,部分框架不支持配置允许跨域,故本文主要介绍第二种方案:nginx反向代理。


nginx反向代理

安装运行nginx

1. 使用docker安装nginx

推荐使用docker安装nginx,可以保证大家使用到一样的程序环境。

a. 先搜索下载安装docker,随后根据后文操作。

Docker官方安装地址

b. 安装好docker后,于项目目录下,执行以下命令。

# 检查docker是否成功安装
docker -v

# 拉取docker镜像
docker pull nginx:1.22

# 创建并运行nginx容器
docker run --name my-nginx -d -p 8080:8080 nginx:1.22

# 复制nginx配置文件至容器内
docker cp nginx.conf my-nginx:/etc/nginx/nginx.conf

# 重启nginx容器
docker restart my-nginx

# 在对配置文件进行更新后,执行后两条命令即可生效(复制配置及重启容器)

扩展知识:Docker 入门教程


2. 在主机直接安装nginx

a. 根据主机系统,在网上搜索安装教程即可。

b. 安装成功后可执行nginx -h,查看默认的配置文件地址。

nginx-help

将以下内容复制到配置文件的http模块内

  server {
listen 8080;

# backend-addr
location /api/ {
proxy_pass http://127.0.0.1:8000;
}


# frontend-addr
location / {
proxy_pass http://127.0.0.1:5500;
}
}

c. 执行nginx启动服务。

d. 在对配置文件进行更新后,执行nginx -s reload即可生效。


测试nginx效果

a. 将index.html里的SERVER_ADDR值,更新为http://127.0.0.1:8080/api/health

b. 打开地址http://127.0.0.1:8080 ,可正常显示前端网页内容。

c. 点击Request,没有再出现跨域报错,正常获取到请求结果。

success

d. 使用vscode的Live Server插件启动前端服务的同学,控制台可能会出现Websocket报错,原因是Live Server热加载的ws连接没有配置代理。

有需要的可以在配置文件server模块中再添加以下语句。

# frontend live reloading
location ~ /ws$ {
proxy_pass http://host.docker.internal:5500;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

至此,成功使用nginx解决跨域问题。