Configuring ActionMailer host and protocol for URL generation

Updated . Posted . Visible to the public. Repeats.

When you generate a URL in a mailer view, ActionMailer will raise an error unless you previously configured it which hostname to use Show archive.org snapshot .

There are two options to set the default_url_options of ActionMailer:

  1. Hardcoded solution (preferred solution when using Rails with ActiveJob/Sidekiq or Cronjobs)
  2. Dynamic solution

1. Hardcoded solution

When you are sending mails from outside the request cycle, e.g. ActiveJob/Sidekiq or Cronjobs, you need to configure the default_url_options in your Rails configuration.

# config/application.rb as fallback/default
Rails.application.default_url_options = { host: 'localhost', port: 3000, protocol: 'http://' }
Rails.application.configure do
 # ...
end

# config/environments/staging.rb
Rails.application.default_url_options = { host: 'staging.example.com', protocol: 'https://' }
Rails.application.configure do
 # ...
end

# config/environments/production.rb
Rails.application.default_url_options = { host: 'www.example.com', protocol: 'https://' }
Rails.application.configure do
 # ...
end

2. Dynamic solution

Configuring the correct hostname is quite annoying when you have multiple deployment targets with different hostnames, e.g. a staging server and a production server. Using the hack below you don't need to configure default url options for your mailers, as these are now derived from the request:

class ApplicationController < ActionController::Base

  before_action :make_action_mailer_use_request_host_and_protocol
  
  private

  def make_action_mailer_use_request_host_and_protocol
    ActionMailer::Base.default_url_options[:protocol] = request.protocol
    ActionMailer::Base.default_url_options[:host] = request.host_with_port
  end

end

If your application server talks only HTTP and relies on the web server for SSL, and if the web server is setting a special HTTPS forwarding header, Rails recognizes that a request originally was on HTTPS and will return the correct protocol.

Caveats:

  • You need to harden your server setup to forbid the routing of a request with the HOST header to an application server, that serves requests under a different host e.g. your application server example.com should not serve requests with the HOST header hacker.xyz
  • You need to take care in your tests, that you reset ActionMailer::Base.default_url_options after e.g. request specs, e.g. spec/support/action_mailer.rb:
RSpec.configure do |config|
  config.around(type: :request) do |example|
    url_options = ActionMailer::Base.default_url_options.dup
    example.run
    ActionMailer::Base.default_url_options = url_options
  end
end
Henning Koch
Last edit
Daniel Straßner
Keywords
route, routing, router
License
Source code in this card is licensed under the MIT License.
Posted by Henning Koch to makandra dev (2011-07-14 13:14)