Define host so absolute urls work in development and test

In development and test, you can’t generate an absolute url without first configuring your host and asset_host. For me, this is always localhost, so it would be nice it that just worked.

Without the host, you get a helpful error: :+1:

ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true

Seems simple, but gets somewhat confusing since this is configured separately for action_mailer urls, action_mailer asset urls and application urls:

# config/environments/development.rb
# config/environments/test.rb

Rails.application.configure do
  ...
  # Action Mailer
  config.action_mailer.default_url_options = { host: 'localhost:3000' } # for absolute urls in email
  config.action_mailer.asset_host = "http://localhost:3000" # for image URLs in HTML email

  # Allow generating absolute urls with routing url helpers.
  Rails.application.routes.default_url_options[:host] = 'localhost:3000'

This is small, but seems a stumbling point for beginners. At least it was once confusing for this one :smile:

12 Likes

Ideas:

  1. Perhaps a single base config. Say config.routes.default_url_options that all others extend? Then the error message could suggest setting :host there. The basic case is now easier.
  2. Perhaps no config for development and test. Just assume localhost:3000. Detect how the server was started rails server -p 1234 or rails server --binding 0.0.0.0 so it “just works”.

If we can do both, development is zero config, production is a one liner. The beginner already has a win before they find themselves looking up docs to get their CDN working, etc.

Can this work?

4 Likes

Yep, have to look this one up again every single time I encounter it.

I like idea #1 better – I worry that #2 might provide a false sense of security and just push the config sadness to “emergency live production debugging.”

@tenderlove or another maintainer – is there any reason that having config.routes.default_url_options set a default for all default_url_options would be Bad?

2 Likes

@Betsy_Haibel, you’re right, and consistent configuration between development/staging/production is a strong protection against a stressful production surprise.

What if rails new also includes it in the generated production configs?

# config/environments/production.rb
  # For generating absolute urls with routing url helpers.
  config.routes.default_url_options[:host] = 'example.com'

Would this be enough protection from that production WTF?

1 Like

I can’t think of why it would be bad off the top of my head. We should try it out and see if any tests give us insight. My gut feeling is we just have two settings because of some kind of “separation” of the frameworks (action pack vs action mailer), but I don’t think that’s a strong reason.

If I can get some time I’ll try to make a PR that does this. It won’t be for a few days though so if someone else wants to try, don’t wait for me!

4 Likes

Will this setting cause Rails to generate absolute URLs all the time? If so, I’m not sure it would be a good default.

2 Likes

I have encountered several application where asset_host was redirecting to a cloudfront or S3 DNS while mailer host was set to the actual application.

I think that is why we need both but I guess that most of the time people only use a single url , we could have some kind of “main” app url and then allow specific mailer url, asset url, controller url, all of which would delegate to main url unless specified ?

No, that would not be gouda! :laughing: It is used by users_url, not users_path. I haven’t traced the codepath, but this makes intuitive sense since :port and :host wouldn’t need to be called when generating a relative path.

@Philippe_Van_Eerdenb I agree this absolutely can’t prevent using separate hosts for assets, mailers and app routes. My proposal only flies if having a shared set of default_url_options wouldn’t prevent overriding these. Folks graduating from the basic case would need to be able to set all of the places as currently.

2 Likes

This is definitely a source of confusion. The information out there is very mixed, so I did some testing to verify…

This currently (at least in master branch) exists as Rails.application.default_url_options (which is actually an alias for Rails.application.routes.default_url_options). :partying_face:

This currently works already in regular controllers / views, except the information is taken from the request rather than from how the server was launched. Unfortunately, that means it does not work in Action Mailer views, since there is no request as such. :frowning_face: I agree it would be nice to have though!

This is a good point. I think we could add a config option similar to config.require_master_key but for default_url_options[:host]. That would also be more reliable than hoping the user bumps into a “Missing host” error on their own in development.

4 Likes

I just noticed DHH opened this issue for Rails 6.2: Add top-level Rails.application.url for canonical reference · Issue #39566 · rails/rails · GitHub

This could be a solution, so folks may want to check it out (and help it along).

2 Likes