环境变量让配置和代码分离,但它不是自动生效的保险箱。一个值是否保密,取决于它在哪里被读取、是否进入构建结果,以及最终发送给了谁。
这类教训在真实世界里反复上演:有人把模型服务的 API Key 直接写进前端代码并推送到公开仓库,几小时后账单多了几千美元——爬虫全天候扫描 GitHub 上的密钥,拿到就能用。更多时候泄漏没这么戏剧化:密钥躺在某个被遗忘的预览部署里,或者出现在报错截图中,安静地等着被发现。理解环境变量的边界,就是理解「什么能公开、什么打死也不能公开」。
先区分两类配置
公开配置可以被访客看到,例如站点名称、公开域名和前端可使用的公开标识。就算它出现在网页源代码里,也不会造成损失。
服务端密钥包括模型服务的 API Key、数据库密码、签名密钥等。这类信息只能在可信的服务端环境使用,不能发送给浏览器。一旦出现在前端代码、HTML 或网络请求里,就等于公开了。
在 Astro 中,PUBLIC_ 前缀表示允许在客户端使用。不要用它保存密钥。
# 可以公开的示例配置
PUBLIC_SITE_NAME=AICookCode
# 仅供具备服务端能力的应用使用,不应进入客户端
MODEL_API_KEY=replace_with_your_own_key
变量名没有 PUBLIC_ 也不意味着绝对不会泄漏。如果服务端把值写进 HTML、日志或 API 响应,访客依然可能读到它。前缀只是框架层面的约定,真正的边界在你的代码逻辑里:这个值最终会不会被送到浏览器?
不同框架的前缀规则大同小异,记住自己用的那一套即可:
| 框架 | 客户端可用的前缀 | 服务端专用 |
|---|---|---|
| Astro | PUBLIC_ |
无前缀 |
| Vite | VITE_ |
无前缀 |
| Next.js | NEXT_PUBLIC_ |
无前缀 |
| 通用 Node 服务 | — | 全部视为服务端 |
规律很明显:凡是标记为「公开」的变量,构建工具会把它打包进前端代码;没有标记的,默认只在服务端可读。选错前缀,等于亲手把密钥送上门。
本地文件不要提交
在 .gitignore 中排除环境文件,同时允许提交仅包含占位值的示例:
.env
.env.*
!.env.example
.gitignore 只影响尚未跟踪的文件。已经被 Git 跟踪的文件不会因为新增规则而自动从历史消失——这是最常见的误解之一。
光有忽略规则还不够,协作者(或者三个月后的你)需要知道这个项目要配哪些变量。维护一份 .env.example,只放变量名和占位值:
# 复制为 .env 后填入真实值,不要提交真实值
PUBLIC_SITE_NAME=AICookCode
MODEL_API_KEY=your_key_here
DATABASE_URL=your_database_url_here
新成员 clone 仓库后,照着 example 复制一份、填上自己的值就能跑起来,既不用猜,也不会有人把真实密钥提交上来。
提交前检查暂存区:
git status
git diff --staged
密钥也可能出现在截图、调试输出、请求示例和错误日志里。检查时不要只盯着 .env。发 issue 贴报错信息之前,先搜一遍有没有把密钥粘进去;截图之前,先确认终端里没有打印出敏感值。
本地多环境也要分开
除了 .env,常见的做法是按环境拆分:
.env:本地开发默认值.env.local:你个人的本地覆盖,不提交.env.production:生产构建用的公开配置(注意:这里只能放公开值)
加载顺序一般是越具体的覆盖越通用的,具体以你用的框架文档为准。关键原则不变:任何名字里带密钥的文件都不进版本控制,生产环境的真实密钥只放在托管平台的设置里。
静态构建与服务端运行不同
静态站点在构建时生成文件,访客访问的是这些已经生成的 HTML、CSS 和 JavaScript。构建环境变量不会自动变成一个运行时服务端接口。
这句话值得展开:静态站点的「构建时」和「运行时」是两个完全不同的时刻。构建时你电脑上的环境变量可以参与生成页面,但一旦 HTML 生成完毕,访客拿到的就是死文件——之后再改环境变量,已经生成的页面不会有任何变化,必须重新构建。
如果你在静态页面里使用密钥生成内容,仍要检查最终产物是否包含密钥或不该公开的数据。一个简单的自查方法:构建完成后,在 dist 目录里搜索你的密钥片段,确认它没有出现在任何输出文件里。
本站只是静态博客,本身不需要模型密钥。未来如果要做 AI 工具,可以单独部署 API 服务,并在对应平台的服务端密钥设置中配置凭据。静态前端只负责调用你自己的 API,密钥永远不出服务端的门。
云端分别设置环境
正式环境和预览环境最好使用不同的凭据与额度。预览部署的访问范围可能不同,也更容易运行尚未验证的代码——用同一套密钥,等于把生产钥匙交给了实验环境。
在托管平台(Cloudflare Pages、Vercel、Netlify 等)设置变量后,根据平台要求重新部署。不要假设修改配置后,已经生成的静态文件会自动变化。大部分平台的流程是:改变量 → 触发重新部署 → 新构建读取新值。
在 CI 里使用密钥时,用平台提供的 Secrets 功能,而不是把密钥写进 workflow 文件:
# GitHub Actions 示例:密钥从 Secrets 读取,不会出现在日志和代码里
- name: Build
env:
MODEL_API_KEY: ${{ secrets.MODEL_API_KEY }}
run: npm run build
构建日志只记录必要状态。不要通过打印全部环境变量来排查配置问题,可以只输出「配置是否存在」这样的布尔值。排查时多问一句:这个日志谁能看到?CI 日志、错误追踪平台的访问范围,往往比你想象的广。
发现泄漏时,先撤销密钥
如果密钥已经进入公开仓库,按这个顺序处理:
- 立刻撤销或轮换:在服务提供方的后台作废旧密钥、生成新密钥,并检查异常用量。这是第一步,而且越快越好。
- 更新所有使用方:本地
.env、云端环境设置、CI Secrets,全部换成新密钥并重新部署。 - 再处理仓库历史:从历史记录中清除密钥(参考 GitHub 的敏感数据移除文档)。注意重写共享仓库历史会影响协作者,应协调完成。
仅删除最新版本中的字符串,无法让已经复制出去的密钥失效。顺序不能反:先止血(撤销),再清创(清理历史)。很多人第一反应是删代码、改历史,结果旧密钥还在外面裸奔。
公开配置、构建配置、运行时密钥,三者各有位置。把这条边界想清楚,比记住某一种文件命名方式更有用。下次新建项目时,先花五分钟定好:哪些变量公开、密钥放哪里、example 文件长什么样——这五分钟能帮你避开未来最贵的那几堂课。
参考:Astro 环境变量 · GitHub 移除敏感数据
继续保持连接.
如果这篇文章对你有帮助,可以用下面的方式继续关注。
RSS 订阅
按自己的节奏阅读更新没有算法排序,用 RSS 获取新文章,不错过每一次实践更新。
微信公众号·AI 煮代码汤
扫码关注,获取 AI、编程和开发实践的最新分享。



