React部署Firebase后无法读取REACT_APP_DB_API密钥的问题
问题背景
我的React应用本地运行正常,通过const currentDBAPI = process.env.REACT_APP_DB_API能正常读取密钥。但部署到Firebase后,React读取密钥时报错:SyntaxError: Unexpected token '<', "<!doctype "... is not valid JSON。
已尝试的操作:
- 参考Firebase相关文档的Secret Manager流程,但该流程针对NodeJS服务端,安装
firebase-functions包后,GitHub Actions部署时出现Module not found: Error: Can't resolve 'async_hooks' in '.../firebase-functions/lib/common'错误,安装async_hooks后问题依旧,后得知该包为NodeJS服务端专用。 - 在GitHub Actions构建步骤中添加密钥配置,但问题仍未解决:
steps: - uses: actions/checkout@v3 - run: yarn install --frozen-lockfile && yarn build env: REACT_APP_DB_API: '${{ secrets.REACT_APP_DB_API }}' - uses: FirebaseExtended/action-hosting-deploy@v0 with: repoToken: '${{ secrets.GITHUB_TOKEN }}' firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_project-id }}' projectId: my-project-id
核心原因
React是前端单页应用,构建后会将环境变量打包进静态文件,无法在运行时直接读取Firebase Secret Manager的密钥,也不能通过服务端的firebase-functions处理前端变量。你遇到的报错,是因为代码误将HTML页面内容(<!doctype开头)当作JSON解析,大概率是读取密钥的逻辑出错,把变量当成API地址发起了请求。
正确解决步骤
1. 确认构建时环境变量注入逻辑
你的GitHub Actions配置方向正确,但需注意两点:
- 确保GitHub仓库的
Secrets中已正确添加REACT_APP_DB_API,密钥值无误。 - 构建命令(
yarn build)必须在注入环境变量的步骤中执行——Create React App会在构建阶段,将所有前缀为REACT_APP_的变量嵌入到静态代码中。
2. 修正代码中读取变量的逻辑
确保没有在运行时发起请求获取该密钥,正确写法是直接引用变量:
const currentDBAPI = process.env.REACT_APP_DB_API; // 直接使用变量,不要用fetch/axios发起请求
3. 验证构建产物
本地测试构建时的变量注入效果:
REACT_APP_DB_API=你的测试密钥 yarn build
打开构建后的build/static/js/main.*.js文件,搜索REACT_APP_DB_API,确认密钥已被替换到代码中。注意:此方式会将密钥明文打包进前端代码,仅适用于非敏感密钥(如公开的API调用密钥);若为敏感密钥(如数据库密码),必须通过Firebase Functions中转。
4. 敏感密钥的中转方案
如果REACT_APP_DB_API是敏感密钥,需通过Firebase Functions做中间层:
- 编写一个Firebase Function作为API代理,在服务端读取Secret Manager的密钥,再转发请求到目标DB API。
- React前端仅请求该Function的地址,不直接接触敏感密钥。
示例Function代码:
const functions = require("firebase-functions"); const { SecretManagerServiceClient } = require("@google-cloud/secret-manager"); const client = new SecretManagerServiceClient(); exports.proxyDBAPI = functions.https.onRequest(async (req, res) => { // 从Secret Manager获取密钥 const [version] = await client.accessSecretVersion({ name: "projects/你的项目ID/secrets/REACT_APP_DB_API/versions/latest", }); const dbApiKey = version.payload.data.toString(); // 转发请求到目标DB API,携带密钥 const response = await fetch("https://你的DBAPI地址/endpoint", { headers: { "Authorization": `Bearer ${dbApiKey}` } }); const data = await response.json(); res.send(data); });
React前端请求示例:
const fetchData = async () => { const response = await fetch("/proxyDBAPI"); const data = await response.json(); // 处理返回数据 };
内容的提问来源于stack exchange,提问作者MadeInDreams

