Read more

Configuring ActionMailer host and protocol for URL generation

Henning Koch
July 14, 2011Software engineer at makandra GmbH

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 .

Illustration UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
Read more 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.
Posted by Henning Koch to makandra dev (2011-07-14 15:14)