こんにちは、駆け出しエンジニアの@kaiです。
私は現在、Railsチュートリアルを進めているのですが、第11章「アカウントの有効化」の「11.4 本番環境のメール送信」を進める中で、本番環境でメールが送れない問題に遭遇しました。同じ問題で困る人がいるかもしれないと思い、注意喚起のために原因と解決策をシェアします。
それまでのあらすじ
Railsチュートリアルでは、第10章まででユーザー認証の機能を持つ簡単なアプリケーションを作り、Render にデプロイしています。
第11章では、そのアプリに「メールによるアカウントの有効化」の機能を実装します。新規ユーザー登録をすると、本番環境では実際に有効化メールが送信され、メール内のリンクをクリックすることで初めてログインできるようになる、という流れです。
このメール送信機能のため、チュートリアルでは Mailgun という有名なメール配信サービスを利用します。
Mailgunのサンドボックス機能
本来、メールの信頼性を担保するにはカスタムドメイン(自分専用の独自のドメイン)が必要ですが、チュートリアルでは簡易的にMailgunの「サンドボックス」という機能で代用します。
これは sandbox...mailgun.org のような仮のドメインを使う機能で、手軽な反面、事前に登録・認証したメールアドレスにしか送信できないという制限があります。
そのため、そもそもアプリの新規ユーザー登録に使用したいメールアドレスが、Mailgunの受信許可リストに登録されていないとメールは届かないので注意しましょう。宛先メールアドレスの登録はMailgunサイト左のナビゲーションから「Send」>「Domain settings」>「Setup」>「Add test email recipient」からできます。
送信元メールアドレスの設定
また、チュートリアルでは、app/mailers/application_mailer.rb で、以下のように送信元(差し出し人)のメールアドレスに「自分が使っているメールアドレス」を指定するよう指示があります。
class ApplicationMailer < ActionMailer::Base
default from: "user@realdomain.com" # 自分の使用しているメールアドレス
layout "mailer"
end
これは user@realdomain.com というアドレスからメールが届く想定ですが、この設定が後ほど説明するメール遅延の原因になります。
問題のコード
さて、チュートリアルにしたがって Mailgun の設定をし、MAILGUN_SMTP_LOGIN と MAILGUN_SMTP_PASSWORD の環境変数を取得したら、チュートリアルでは config/environments/production.rb に次のような記述を書くように書いてあります。
Rails.application.configure do
#...
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
host = 'https://<あなたのRenderアプリ名>.onrender.com'
config.action_mailer.default_url_options = { host: host }
ActionMailer::Base.smtp_settings = {
:port => 587,
:address => 'smtp.mailgun.org',
:user_name => ENV['MAILGUN_SMTP_LOGIN'],
:password => ENV['MAILGUN_SMTP_PASSWORD'],
:domain => host,
:authentication => :plain,
}
#...
end
ですが、このままRenderにデプロイし、新規ユーザー登録をしようとすると、Railsのエラー画面に飛ばされてしまいます。
このとき、もちろんアカウント有効化のメールは届かず、MailgunのDashboardを見てみると、そもそもメール送信のリクエストさえMailgunには届いていないことがわかります。
RenderのLogを読んでみよう
何が起きているか、RenderのLogを見てみましょう(解決法だけ知りたい方は次章へ飛んでください)。
新規ユーザー登録フォームを送信した際のログは、以下のようになっていました(一部抜粋・整形)。
Started POST "/users"
Processing by UsersController#create as TURBO_STREAM
Parameters: {
"authenticity_token"=>"[FILTERED]",
"user"=>{
"name"=>"(入力したユーザ名)",
"email"=>"(入力したメールアドレス)",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"
},
"commit"=>"Sign up"
}
Rendered layout layouts/mailer.html.erb
Rendered layout layouts/mailer.text.erb
# --ここでエラー発生--
Completed 500 Internal Server Error
Net::OpenTimeout (execution expired):
app/models/user.rb:67:in 'send_activation_email'
app/controllers/users_controller.rb:23:in 'create'
このログを順を追って見ていきましょう。
Started POST "/users"
Processing by UsersController#create as TURBO_STREAM
Parameters: {
"authenticity_token"=>"[FILTERED]",
"user"=>{
"name"=>"(入力したユーザ名)",
"email"=>"(入力したメールアドレス)",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"
},
"commit"=>"Sign up"
}
まずこのLogから、新規ユーザー登録フォームを送信した際、しっかりと正しいパラメータで新規ユーザー登録の処理(Users コントローラーの create アクション)が呼び出せているようです。
この create アクションの中では、入力されたユーザー情報のバリデーションに成功すると send_activation_email というメソッドを呼び出し、本質的には次のメソッドが実行されます。
UserMailer.account_activation(@user).deliver_now
実際、先ほどのLogの続きでは
Rendered layout layouts/mailer.html.erb
Rendered layout layouts/mailer.text.erb
となっているので、確かに UserMailer.account_activation が呼び出されてメールのテンプレートを準備していることがわかります。
そして問題のエラーはその次です。
Completed 500 Internal Server Error
Net::OpenTimeout (execution expired):
app/models/user.rb:67:in 'send_activation_email'
app/controllers/users_controller.rb:23:in 'create'
このLogを見るに、エラーは、app/controllers/users_controller.rb 23行目のcreate アクションから呼び出された、 app/models/user.rb 67行目のsend_activation_email メソッドの内部で発生しています。
また Net::OpenTimeout は、Rubyが外部のネットワークサービス(この場合はMailgun)に接続しようとしたものの、一定時間内に応答がなかった場合に発生します。
つまり以上の情報をまとめると、「新規ユーザー登録の処理は正しく呼び出せているけど、その処理の中でMailgunサーバーに接続できていない」 ということが推測できます。
ということで、先ほどのMailgunへ接続するためのコードを修正しましょう。
コードを修正する
では、チュートリアルで提示されているコードを修正します。
Rails.application.configure do
#...
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
host = 'https://<あなたのRenderアプリ名>.onrender.com'
config.action_mailer.default_url_options = { host: host }
ActionMailer::Base.smtp_settings = {
:port => 587, # ← ここが原因
:address => 'smtp.mailgun.org',
:user_name => ENV['MAILGUN_SMTP_LOGIN'],
:password => ENV['MAILGUN_SMTP_PASSWORD'],
:domain => host,
:authentication => :plain,
}
#...
end
この設定にはいくつかツッコミどころがあるのですが、エラーの最も本質的な原因はポート番号が587であることです。
実は調べたところ、Renderは2025年9月16日に、スパムメール送信対策として無料プランにおけるポート 25, 465, 587 を使用した外部のSMTPサーバーへの接続がすべてブロックされる仕様に変更されています。😱
ということで、私が記事を書いている2025年10月現在、Renderの無料プランでは、チュートリアル通りにMailgunの 587 番ポートへ接続しようとしても、Render側でブロックされてしまうためタイムアウトエラーになっていた、という訳です。
2525番ポートで回避する
「じゃあRenderを有料プランにしないとダメなのか…」と思うかもしれませんが、実は回避方法があります。それは、Mailgunの 2525 番ポートを使用することです!
幸い、Mailgunは 25, 465, 587 以外にも 2525 番ポートでもリクエストを受けており、これは先日のRenderのブロック対象に入っていないため、無事Mailgunに接続することができます!🎉
ということで、次のようにポート番号を修正すればひとまずエラーは解消されます。
Rails.application.configure do
#...
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
host = 'https://<あなたのRenderアプリ名>.onrender.com'
config.action_mailer.default_url_options = { host: host }
ActionMailer::Base.smtp_settings = {
:port => 2525, # 587はもう使えないので2525で代用します
:address => 'smtp.mailgun.org',
:user_name => ENV['MAILGUN_SMTP_LOGIN'],
:password => ENV['MAILGUN_SMTP_PASSWORD'],
:domain => host,
:authentication => :plain,
}
#...
end
これでタイムアウトエラーは解消され、Mailgunにリクエストが届くようになります。
もう少しだけ修正してみる
ただし! この「修正版1」のままだと、確かにエラーは解消されますが、実際にメールが届くのにとんでもなく時間がかかります(私は30分くらいかかりました...)。
正直、アカウントを有効化するためのメールが届くのに30分もかかっていたら、アプリが正常に動いているのかどうかも判別できないので、この点も修正することをおすすめします。
なぜメールが遅延するのか?
この遅延の原因は、SMTPサーバーに名乗るドメイン (:domain) と、メールの差出人 (from:) のドメインが食い違っていることです。
もう一度、今の設定を見てみましょう。
-
:domain:production.rbではhost変数をそのまま使っているので、アプリのURL(...onrender.com)を指定しています -
from::application_mailer.rbではチュートリアル通り、自分のメールアドレス(例: user@gmail.com など)を指定しています
:domain は、メール通信の中で「このメールを送信しているのはどのドメインのサーバーか」をメールを受信するサーバーに伝えるための設定です。
そのため現状では、メールを送信するときに、Mailgunサーバーに「私は onrender.com です」と名乗っているのに、メール自体は「user@gmail.comからです」と主張している、ようなちぐはぐな状態です。
その結果、多くのメールサービス(GmailやiCloudメール)はそのメールを「なりすましメール」と強く疑い、なかなかメールを受信してくれません。
これが「送信はできるけど、受信にはめちゃくちゃ時間がかかる」現象の正体です。
ドメインをMailgunに統一する
解決策はシンプルで、 解決策はシンプルで、:domain と from: の両方で、Mailgunのサンドボックスのドメインを使うことです。
というか、そもそもサンドボックスのドメインはこのような問題を避けるために提供されているものです。
Rails.application.configure do
#...
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
host = 'https://<あなたのRenderアプリ名>.onrender.com'
config.action_mailer.default_url_options = { host: host }
ActionMailer::Base.smtp_settings = {
:port => 2525,
:address => 'smtp.mailgun.org',
:user_name => ENV['MAILGUN_SMTP_LOGIN'],
:password => ENV['MAILGUN_SMTP_PASSWORD'],
:domain => 'sandbox...mailgun.org', # ドメインをmailgunに統一
:authentication => :plain,
}
#...
end
class ApplicationMailer < ActionMailer::Base
default from: "noreply@sandbox...mailgun.org" # ドメインをmailgunに統一
layout "mailer"
end
このコードでは、わかりやすいさのためにサンドボックスのドメインをハードコーディングしていますが、ログインIDやパスワードと同じように環境変数にしておくと管理がより楽になります。
こうすることで、メールを送信するサーバーのドメインと、差出人メールアドレスのドメインが一致するため、メールサービスの審査をスムーズに通過し、すぐにメールを受け取ることができます。
最終的なコード
現時点のコードでも動作に支障はないのですが、最後にコードをもう少しだけ綺麗にします。
先ほどの production.rb の :domain を修正したことで、アプリのURLを変数 host に入れて使い回す必要がなくなりました。
加えて、default_url_options の host: に「https://」を含む完全なURLを指定するのは少し冗長なため、protocol オプションを使って分離する方が、Railsのコードとして一般的です。
Rails.application.configure do
#...
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.default_url_options = {
host: "<あなたのRenderアプリ名>.onrender.com",
protocol: "https" # protocolとhostは分けるのがオススメ
}
ActionMailer::Base.smtp_settings = {
:port => 2525,
:address => 'smtp.mailgun.org',
:user_name => ENV['MAILGUN_SMTP_LOGIN'],
:password => ENV['MAILGUN_SMTP_PASSWORD'],
:domain => 'sandbox...mailgun.org',
:authentication => :plain,
}
#...
end
これで修正は完了です!お疲れ様でした!
最後に
Railsチュートリアルも終盤ですが、進めれば進めるほど、こうした外部サービスの仕様変更つまずくポイントも増えてきますね(説明もだんだん雑になっているような...)。
まあでも、認証機能をライブラリを使わずに実装するのは、Railsに限らずバックエンドの勉強になるので、このまま最後まで走り抜けたいと思います。
この記事が、同じエラーで困っている方の助けになれば幸いです。
というわけで皆さんもRailsチュートリアル一緒に頑張っていきましょう!