You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部署在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 06:16:04