部署在Google Cloud Run的Node.js tRPC服务器出现CORS错误
问题:Google Cloud Run部署Node.js tRPC应用遭遇CORS错误
场景还原
本地运行正常的Node.js tRPC应用,部署到Google Cloud Run后触发CORS错误,提示**Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.**。
初始main.ts代码(含WebSocket):
import { createHTTPServer } from '@trpc/server/adapters/standalone'; import { applyWSSHandler } from '@trpc/server/adapters/ws'; import { WebSocketServer } from 'ws'; import cors from 'cors'; import { AppRouter, appRouter } from './routers/app.router'; import { createContext } from './trpc'; // http server const { server, listen } = createHTTPServer({ middleware: cors(), router: appRouter, createContext }); // ws server const wss = new WebSocketServer({ server }); applyWSSHandler<AppRouter>({ wss, router: appRouter, createContext }); listen(8080); console.log("tRPC listening on port 8080");
尝试改用Express+HTTPS并移除WebSocket后,问题依旧:
import cors from 'cors'; import { AppRouter, appRouter } from './routers/app.router'; import { createContext } from './trpc'; import * as fs from 'fs'; import * as trpcExpress from '@trpc/server/adapters/express'; import express from 'express'; import * as https from 'https'; const PORT = 8080; const key = fs.readFileSync('/selfsigned.key'); const cert = fs.readFileSync('/selfsigned.crt'); const credentials = { key: key, cert: cert }; const app = express(); app.use(cors()); app.use( '/', trpcExpress.createExpressMiddleware({ router: appRouter, createContext }) ); const httpsServer = https.createServer(credentials, app); httpsServer.listen(PORT);
Cloud Run服务可正常启动,但CORS错误持续存在。
核心原因与解决方案
1. 无需在应用内配置HTTPS,Cloud Run已代理处理
Cloud Run会自动为服务提供合法的HTTPS证书并做反向代理,你在应用内自行配置自签名证书完全多余,反而可能导致请求头处理异常。
修复动作:
- 删除所有HTTPS相关代码,恢复为HTTP监听8080端口(Cloud Run会自动完成HTTPS转换)。
2. CORS中间件需显式指定允许的Origin
默认cors()配置在本地环境可通用,但Cloud Run的代理层会对请求头做处理,模糊的Origin配置容易被覆盖。
修复动作:
- 明确指定允许的前端域名,而非依赖默认通配符:
// Express版本 app.use(cors({ origin: 'https://your-frontend-domain.com', // 替换为实际前端域名 credentials: true, // 若需携带Cookie/认证信息则开启 allowedHeaders: ['Content-Type', 'Authorization'], methods: ['GET', 'POST', 'OPTIONS'] })); // tRPC standalone版本 const { server, listen } = createHTTPServer({ router: appRouter, createContext, middleware: cors({ origin: 'https://your-frontend-domain.com', credentials: true }) }); - 多域名场景可使用动态判断:
app.use(cors({ origin: (origin, callback) => { const allowedOrigins = ['https://foo.com', 'https://bar.com']; // 允许无Origin的请求(如Postman) if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error('Not allowed by CORS')); } } }));
3. 确保OPTIONS请求被正确处理
浏览器发起的预请求(OPTIONS)需被正确响应,部分情况下tRPC或Express的默认配置可能遗漏处理。
修复动作:
- 手动添加OPTIONS请求处理逻辑(适用于standalone服务器):
const { server, listen } = createHTTPServer({ router: appRouter, createContext, middleware: async (req, res, next) => { if (req.method === 'OPTIONS') { res.setHeader('Access-Control-Allow-Origin', 'https://your-frontend-domain.com'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); res.writeHead(200); res.end(); return; } await cors({ origin: 'https://your-frontend-domain.com' })(req, res, next); } });
4. 清除浏览器缓存或用无痕模式测试
浏览器会缓存CORS错误响应,新配置生效后可能仍显示旧错误。
修复动作:
- 清除浏览器缓存,或使用无痕窗口发起请求测试。
内容的提问来源于stack exchange,提问作者serlingpa
相关产品推荐
相关产品推荐

