在Heroku部署Rails+AngularJS,能否为生产与staging设不同BASE_URL环境变量?
Rails+AngularJS 环境变量BASE_URL生产环境异常排查与解决
核心问题
- 手动配置了生产/Staging环境不同的
BASE_URL环境变量,控制台显示变量值正确,但生产环境代码实际重定向到Staging环境 - 怀疑是Heroku流水线
promote功能同步代码导致,同时担心旧版AngularJS的资产预编译问题
排查方向
1. 验证Heroku Promote的资产同步情况
- 查生产环境发布来源:执行
heroku releases -a <生产应用名>,看最新发布是不是从Staging promote过来的,如果是,说明预编译的资产是Staging版本 - 对比资产内容:用
heroku run cat public/assets/config.<hash>.js -a <生产应用名>和Staging环境的对应文件对比,确认资产里是不是硬编码了Staging的BASE_URL
2. 检查Rails资产预编译的环境依赖
- 旧版Rails资产预编译默认用编译时的环境变量,不是运行时。如果在Staging预编译后promote到生产,资产里的
BASE_URL会是Staging的值,因为预编译时读的是Staging的变量 - 看
config/environments/production.rb里的config.assets.compile配置,如果是false(生产默认值),说明资产是预编译好的,运行时不会重新编译
3. 排查AngularJS的变量注入方式
- 如果是通过ERB模板(比如
app/config.js.erb里写var BASE_URL = '<%= ENV["BASE_URL"] %>';)注入变量,这个值是预编译时固定的,运行时不会读环境变量 - 检查前端代码里
BASE_URL的获取逻辑:如果是直接读预编译后的静态文件,而不是运行时从后端拿,就会出现promote后值不更新的问题
4. 确认Heroku环境变量的有效性
- 再验证生产环境的
BASE_URL:执行heroku config:get BASE_URL -a <生产应用名>,确保值正确 - 检查是否有其他配置覆盖:比如
config/secrets.yml、config/application.yml(如果用了figaro)里有没有硬编码Staging地址
解决方案
方案1:改成运行时获取环境变量
- 后端加个接口
GET /api/config,返回当前环境的BASE_URL,AngularJS启动时调用这个接口拿值,不用预编译后的静态文件里的硬编码值 - 如果必须预编译,在Heroku生产环境单独执行预编译:在
Procfile里加release: bundle exec rake assets:precompile,确保每次部署(包括promote后)都重新预编译(注意会增加部署时间)
方案2:调整Heroku流水线的Promote行为
- 关闭资产同步:在Heroku流水线设置里取消勾选"Promote slug including assets",这样promote只同步代码,生产环境会重新预编译资产
- 用占位符替换:预编译时把
BASE_URL设为{{BASE_URL}},然后在Heroku的release阶段用sed替换成实际值,比如在Procfile里加release: sed -i 's/{{BASE_URL}}/'$BASE_URL'/g' public/assets/config*.js
方案3:换用meta标签注入变量
- 在Rails的
application.html.erb里加<meta name="base-url" content="<%= ENV["BASE_URL"] %>">,然后AngularJS里用document.querySelector('meta[name="base-url"]').content获取值,这样值是页面渲染时生成的,不受预编译资产影响
内容的提问来源于stack exchange,提问作者bluemihai
相关产品推荐
相关产品推荐

