Dynamically setting Rails
default_url_options in Heroku review apps
I love Heroku review apps but not when URLs in emails point to the parent app – and if you have a hypermedia API its
_links can end up totally broken. Why does this happen and how can it be fixed?
My usual approach used to be to configure
Rails.application.routes.default_url_options[:host] from a
DEFAULT_URL_HOST environment variable, but if the review app inherits this variable then all of its URLs generated outside the controller/view request/response cycle – including URLs in emails and serializers such as ActiveModel::Serializers – incorrectly point to the parent app. In the past I’ve been forced to manually update a review app’s config but that isn’t a solution.
It isn’t possible to dynamically set an environment variable but you can detect a review app at run-time by enabling the
HEROKU_APP_NAME environment variable and then use it to generate the correct host. To do this in Rails create an initializer
config/initializers/default_url_options.rb with the following contents:
# If a default host is specifically defined then it's used otherwise the app is
# assumed to be a Heroku review app. Note that `Hash#fetch` is used defensively
# so the app will blow up at boot-time if both `DEFAULT_URL_HOST` and
# `HEROKU_APP_NAME` aren't defined.
host = ENV['DEFAULT_URL_HOST'] ||
# Set the correct protocol as SSL isn't configured in development or test.
protocol = Rails.application.config.force_ssl ? 'https' : 'http'
In staging and production apps you should set the
heroku config:set, but in review apps tell Heroku to pick up the
HEROKU_APP_NAME variable by adding it to the app’s
app.json (and remove references to
Note that this also means in development/test/CI you’ll need to define the
DEFAULT_URL_HOST environment variable or the app won’t boot – on my development machine I use direnv, dotenv is also popular.
And that’s it, now all URLs generated in emails and serializers will correctly point to the review app’s host.