Express+Nginx环境下CORS拦截与502错误排查求助
问题概述
部署在Nginx反向代理后的ExpressJS应用,已配置cors模块实现跨域,但通过localhost:3000客户端多次发送POST文件上传请求后,会随机出现CORS拦截错误,同时伴随Nginx返回的502 Bad Gateway错误。排查发现应用能正常执行到业务逻辑someFunction,PM2状态正常,但响应返回环节失败,且Nginx日志中出现来源不明的GET /?name=example.com&type=A请求。
当前配置
Express的CORS配置
var express = require("express"); var cors = require("cors"); var app = express(); var corsOptions = { origin: "http://localhost:3000", credentials: true, optionSuccessRate: 200 }; app.use(cors(corsOptions)); app.options("*", cors());
Nginx配置
/etc/nginx/nginx.conf
user www-data; worker_processes auto; pid /run/nginx.pid; error_log /var/log/nginx/error.log; include /etc/nginx/modules-enabled/*.conf; events { worker_connections 768; # multi_accept on; } http { sendfile on; tcp_nopush on; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE ssl_prefer_server_ciphers on; access_log /var/log/nginx/access.log; gzip on; include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; client_max_body_size 100M; }
/etc/nginx/sites-available/default
server { server_name DOMAIN_NAME; location / { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_redirect off; proxy_request_buffering off; } listen [::]:443 ssl ipv6only=on; # managed by Certbot listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/[DOMAIN_NAME]/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/[DOMAIN_NAME]/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } server { if ($host = DOMAIN_NAME) { return 301 https://$host$request_uri; } # managed by Certbot listen 80 default_server; listen [::]:80 default_server; server_name DOMAIN_NAME; return 404; # managed by Certbot }
Express应用核心代码
import fs from "fs"; import path from "path"; import cors from "cors"; import express from "express"; import formidable from "formidable"; import someFunction from "./someFunction.js"; const app = express(); var corsOptions = { origin: "http://localhost:3000", credentials: true, optionSuccessStatus: 200 }; app.use(cors(corsOptions)); app.options("*", cors()); const port = 5000; const __dirname = path.resolve(); app.post("/", function(req, res, next) { const form = formidable({ uploadDir: "uploads" }); form.parse(req, function(err, fields, files) { try { if(err) { next(err); res.send("Error"); } const metadata = JSON.parse(fields["config"][0]); const dir = `${form.uploadDir}/${metadata.dir}`; const fileName = files["files"][0].originalFilename; fs.mkdirSync(dir); fs.rename(files["files"][0].filepath, `${dir}/${fileName}`, function(err) { if(err) throw err; }); // do conversion here------------------------- someFunction(fileName, metadata) .then(() => { res.write("Done"); res.end(); }) .catch(er => { throw er; }); //-------------------------------------------- }catch(e) { console.log(e); res.write("Error"); res.end(); } }); }); app.listen(port, function() { console.log("Server initialized"); });
出现的错误
Access to XMLHttpRequest at SERVER_DOMAIN from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
POST SERVER_DOMAIN net::ERR_FAILED 502 (Bad Gateway)
排查发现的异常
- 应用能正常执行到
someFunction,但调用res.write返回响应时失败 - Nginx日志中出现异常请求:
request: "GET /?name=example.com&type=A HTTP/1.1", upstream: "http://127.0.0.1:5000/?name=example.com&type=A", host: "........."
问题原因分析
- Express未处理异步/同步异常,导致进程无响应:
fs.mkdirSync(dir)若目录已存在会抛出异常,虽有try/catch,但fs.rename回调内的throw err无法被外层try/catch捕获,导致Express未处理该错误,进程挂起或崩溃,Nginx返回502。someFunction的catch块中throw er会抛出未捕获异常,同样导致进程异常。
- CORS错误是502的次生问题:Nginx返回502时,不会携带Express设置的CORS响应头,浏览器因此触发CORS拦截提示,混淆了问题本质。
- 异常GET请求来源:大概率是DNS探测工具或爬虫发送的请求,虽不直接导致502,但暴露了应用未处理非POST请求的漏洞。
修复方案
1. 完善Express错误处理逻辑
修改POST接口代码,解决未捕获异常问题:
app.post("/", function(req, res) { const form = formidable({ uploadDir: "uploads" }); form.parse(req, function(err, fields, files) { // 优先处理表单解析错误 if(err) { console.error("表单解析错误:", err); return res.status(400).send("表单解析失败"); } try { const metadata = JSON.parse(fields["config"][0]); const dir = `${form.uploadDir}/${metadata.dir}`; const fileName = files["files"][0].originalFilename; // 创建目录时允许递归创建,避免已存在报错 fs.mkdirSync(dir, { recursive: true }); // 使用Promise版rename替代回调,统一异步错误处理 fs.promises.rename(files["files"][0].filepath, `${dir}/${fileName}`) .then(() => someFunction(fileName, metadata)) .then(() => { res.send("处理完成"); }) .catch(er => { console.error("业务处理错误:", er); res.status(500).send("业务处理失败"); }); }catch(e) { console.error("请求处理错误:", e); res.status(500).send("请求处理失败"); } }); });
- 核心优化点:
- 提前处理表单解析错误,直接返回响应
fs.mkdirSync添加{ recursive: true }参数,避免目录已存在抛出异常- 用
fs.promises.rename替代回调式rename,确保异步错误能被捕获 - 所有错误都在catch块中处理,返回明确的错误状态码和信息,不抛出未捕获异常
- 替换
res.write为res.send,简化响应逻辑
2. 优化Nginx配置,增强稳定性
在location块中添加超时配置和全局CORS头,确保错误场景下也能返回跨域头:
location / { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_cache_bypass $http_upgrade; proxy_redirect off; proxy_request_buffering off; # 配置超时时间(根据业务逻辑调整) proxy_connect_timeout 30s; proxy_send_timeout 30s; proxy_read_timeout 60s; # 强制添加CORS头,即使返回错误状态码 add_header Access-Control-Allow-Origin http://localhost:3000 always; add_header Access-Control-Allow-Credentials true always; }
- 核心优化点:
always关键字确保Nginx返回任何状态码时都携带CORS头,避免浏览器触发不必要的CORS拦截- 配置合理的超时时间,适配业务逻辑的执行时长
3. 处理非POST请求
添加全局404处理,避免异常请求引发错误:
app.all("*", (req, res) => { res.status(404).send("接口不存在"); });
内容的提问来源于stack exchange,提问作者Waleed93
相关产品推荐
相关产品推荐

