AWS CloudFront+S3前端访问EC2 Node服务出现CORS错误如何解决
技术栈
Client:React, Redux, axiosServer:AWS-EC2, Route 53, S3, CloudFront, NodeJS, express
第一步,我在Route 53购买了域名,已完成ACM证书签发。
第二步,我将前端构建文件上传到S3存储桶,配置为静态网站。
第三步,将Route 53域名和S3存储桶关联到CloudFront。
第四步,为EC2配置了ELB负载均衡和弹性公网IP(EIP)。
第五步,EC2上部署了Node.js express服务。
第六步,在CloudFront配置了重定向规则:将S3的www.domain.link 请求重定向到 domain.link。
存在问题的客户端和服务端代码如下:
Client.js
import axios from "axios"; import { TYPES, MAF } from "./types"; const API_AUTH = "https://www.domain.link/auth"; const API_USER = "https://www.domain.link"; //必填!! axios.defaults.withCredentials = true; export function loggedIn(data) { return (dispatch) => { axios.post(`${API_AUTH}/login`, data).then((res) => { console.log(res); dispatch({ type: TYPES.LOGIN_SUCCESS, // payload: res.data.userData, }); dispatch({ type: MAF.HIDE_MODAL, }); }); }; } export function register(data) { return (dispatch) => { axios.post(`${API_AUTH}/register`, data).then((res) => { dispatch({ type: TYPES.REGISTER_SUCCESS, payload: res.data, }); }); }; }
./routes/user.js
const router = require("express").Router(); const { login, register, logout, profile, } = require("../controller/userController/userController"); const { authorization } = require("../config/JWTConfig"); router.post("/auth/login", login); router.post("/auth/register", register); router.get("/auth/logout", authorization, logout); router.get("/auth/profile", authorization, profile); module.exports = router;
./app.js
const express = require("express"); // const passportConfig = require("./passport/index"); const passport = require("passport"); const http = require("http"); const https = require("https"); const path = require("path"); const fs = require("fs"); const cors = require("cors"); const cookieParser = require("cookie-parser"); const logger = require("morgan"); require("dotenv").config(); const authRoute = require("./routes/users"); const mainRoute = require("./routes/main"); const port = process.env.PORT || 3000; const app = express(); const whitelist = [ "http://localhost:3000", "http://*.doamin.link", "http://doamin.link", "http://doamin.link/*", ]; const corsOption = { origin: function (origin, callback) { if (whitelist.indexOf(origin) !== -1 || !origin) { callback(null, true); } else { callback(new Error("Not allowed by CORS")); } }, credentials: true, methods: ["GET", "POST", "PUT", "DELETE", "OPTION"], }; app.use(cookieParser()); app.use(logger("dev")); app.use(cors(corsOption)); app.use(express.urlencoded({ extended: false })); app.use(express.json()); app.use(mainRoute); app.use(authRoute); let server; if (fs.existsSync("./cert/key.pem") && fs.existsSync("./cert/cert.pem")) { const privateKey = fs.readFileSync(__dirname + "/cert/key.pem", "utf8"); const certificate = fs.readFileSync(__dirname + "/cert/cert.pem", "utf8"); const credentials = { key: privateKey, cert: certificate }; server = https.createServer(credentials, app); server.listen(port, () => console.log("https server Running")); } else { server = app.listen(port, () => { console.log(`http server Running`); }); } module.exports = server;
在Postman测试或者浏览器点击登录按钮时,会出现如下错误:
Access to XMLHttpRequest at 'https://www.domain.link/login' from origin 'https://domain.link' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
createError.js:16 Uncaught (in promise) Error: Network Error at e.exports (createError.js:16) at XMLHttpRequest.p.onerror (xhr.js:84)
访问domain.link、www.domain.link 都会触发上述错误。
Postman测试截图

修复方案
1. 修复服务端CORS配置错误
你现有CORS配置存在4个问题:
- 域名拼写错误:
doamin.link应为domain.link - 协议不匹配:网站已启用HTTPS,但白名单写的是HTTP开头的域名
- 预检方法拼写错误:
OPTION应为OPTIONS - cors包不支持直接用
*.domain.link这种通配符匹配规则
将app.js的CORS配置替换为如下内容:
const whitelist = [ "http://localhost:3000", "https://domain.link", "https://www.domain.link" ]; const corsOption = { origin: function (origin, callback) { if (whitelist.includes(origin) || !origin) { callback(null, true); } else { callback(null, false); } }, credentials: true, methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowedHeaders: ["Content-Type", "Authorization"] }; // 单独处理所有预检请求 app.options('*', cors(corsOption));
2. 修正API请求路由
你当前前端API请求直接打到了前端对应的CloudFront/S3地址,根本没有转发到EC2服务,二选一选择以下方案:
- 方案1:单独配置
api.domain.link二级域名解析到EC2的ELB,前端API_BASE修改为https://api.domain.link - 方案2:在CloudFront新增行为,路径规则设置为
/auth/*,源站选择EC2对应的ELB,允许GET、POST、OPTIONS方法,配置转发Origin、Access-Control-Request-Headers、Access-Control-Request-Method头,禁用该路径的缓存
3. 修复前端API地址
配合上面的路由配置,将Client.js的API地址修改为同源路径(适配方案2)或者新的API二级域名(适配方案1):
// 适配CloudFront路径转发,直接用同源路径避免跨域 const API_AUTH = "/auth"; const API_USER = "/";
4. 验证CloudFront配置
- 确认仅前端静态资源路径配置了www到根域名的重定向,API路径不会被重定向
- 确认ACM证书覆盖了所有用到的域名
- 确认CloudFront对应API路径的行为没有缓存预检请求
内容的提问来源于stack exchange,提问作者spec
相关产品推荐
相关产品推荐

