本文へスキップ
AI開発ログ

【トラブル解決】Railwayからメールが送れない…原因は「SMTPポートの遮断」でした


代理店向けのWebシステムに、問い合わせ通知メールの機能を実装しました。プログラムはすんなり完成。あとは契約済みのレンタルサーバーのメールアカウントに繋ぐだけ……のはずでした。

「あれ? 送信できない……」

設定は何度見直しても合っている。それなのに、テスト送信を押すと必ずタイムアウトする。ここから数時間の格闘が始まりました。結論から言うと原因はコードでも設定でもなく、PaaS側がSMTP通信そのものを遮断していたというものでした。同じ構成でハマる人は多そうなので、切り分けの過程ごと残しておきます。

やりたかったこと

実装したのは、よくある通知メールの機能です。

  • 問い合わせが入ったら担当代理店へ通知(個人情報は本文に含めない)
  • 同じ問い合わせを本部にも通知
  • 本部からのお知らせを全代理店へ一斉送信
  • パスワード再設定リンクの送信

メールアドレスはすでに持っているので、そのSMTPサーバーに繋ぐだけ。10分で終わる作業だと思っていました。

問題1:設定は正しいのにタイムアウトする

ホスト・ポート・ユーザー・パスワードを環境変数に設定してテスト送信。返ってきたのは timed out だけ。

まず疑うのは設定ミスです。でも手元のPCから同じサーバーの587番ポートに繋ぐと、あっさり応答が返ってきました。

port 587: 接続OK  220 sv****.example.jp ESMTP Postfix

設定は合っている。アプリのサーバーからだけ届かない。 ここが分かれ道でした。

問題2:もっともらしい仮説にハマる

アプリのコンテナから外向きのIPアドレスを調べたところ、アメリカのIPでした。

ここで思いつきます。「国内のレンタルサーバーは、海外からの接続を初期設定でブロックしていることがある。国外IPアクセス制限に弾かれているのでは?」

筋は通っています。実際そういう仕様のサーバーは多い。危うくレンタルサーバー側の設定を触り始めるところでした。

でも、これは外れでした。

決め手になったのは、まったく関係ない別のSMTPサーバー(Gmail)に繋いでみるという検証でした。

smtp.gmail.com への接続
  port 587: Network is unreachable
  port 465: Network is unreachable
  port 25 : Network is unreachable

宛先を変えても同じ。しかもエラーが timed out ではなく Network is unreachable、つまりネットワークの経路そのものが存在しない状態でした。

これで確定です。PaaS側が、宛先を問わずSMTPポート(25 / 465 / 587)を遮断していました。 迷惑メールの踏み台として悪用されるのを防ぐため、多くのPaaSが標準でこの制限をかけています。レンタルサーバーは無実でした。

もしあのまま国外IP制限を疑い続けていたら、何時間も溶かしていたはずです。「同種の別サービスで再現するか試す」のは、原因がこちら側か向こう側かを一発で切り分けられる手段でした。仮説がもっともらしいほど、この検証を挟む価値があります。

解決策を検討する

SMTPが使えない以上、別の経路が必要です。ここで重要なのは、HTTPS通信は普通に通っていたという点。塞がれているのはメール専用のポートだけでした。

案A:メール配信APIを使う

ResendやSendGridなど、HTTPS経由でメールを送れるサービスを使う方法。王道です。ただ無料枠には日次上限があります。

サービス無料枠
Resend1日100通(月3,000通)
SendGrid1日100通
Brevo1日300通

ネックだったのは、開発中のテスト送信でもこの枠を消費すること。動作確認を繰り返すと1日100通はあっという間です。有料プランは月20ドル前後と決して高くありませんが、複数システムへ横展開する前提だと悩ましいところでした。

案B:自前のレンタルサーバーを中継役にする(採用)

ここで気づきました。すでに契約しているレンタルサーバーが遊んでいる。 メール用にしか使っていませんでしたが、PHPが動くWeb領域も付いてきます。

【これまで】
  アプリ ──SMTP──→ [PaaSが遮断] ──→ メールサーバー

【変更後】
  アプリ ──HTTPS──→ レンタルサーバー上のPHP ──→ メール送信
          (普通のWeb通信なので通る)

レンタルサーバーに50行ほどのPHPスクリプトを1つ置き、アプリからは「この宛先に、この件名で、この本文を送って」とHTTPSで依頼するだけ。実際の送信はレンタルサーバーが行うので、PaaSの制限を回避できます。

そして送信上限が桁違いでした。

送信経路1日あたりの上限
メール配信サービス(無料枠)100〜300通
国内レンタルサーバー10,000〜15,000通

すでに払っている費用の範囲内で、無料枠の数十倍。追加費用ゼロで解決しました。

問題3:中継スクリプトは踏み台になり得る

この構成には落とし穴があります。「URLを叩けば誰でもメールを送れる仕組み」をインターネット上に公開することになるという点です。対策なしでは、迷惑メールの発射台を自分で設置するようなものです。

そこで以下を実装しました。

  • 共有トークンによる認証:ヘッダに秘密の文字列を入れ、一致しないリクエストは拒否。比較にはタイミング攻撃に強い関数を使用
  • 差出人アドレスをスクリプト側で固定:依頼する側から差出人を指定できない設計にする
  • 1時間あたりの送信数制限:トークンが漏れた場合の被害を抑える保険
  • ヘッダインジェクション対策:宛先や件名に改行が混入したリクエストを拒否

特に2つ目が効きます。「誰に送るか」は依頼側が決めるが、「誰として送るか」はサーバー側で固定する。 これだけで、万一トークンが漏れても他人になりすましたメールは送れません。悪用時の被害が大きく変わります。

問題4:SPFの不一致という罠

途中で「別のレンタルサーバーから送ってもいいのでは」という案も出ましたが、これには注意が必要でした。

送信元アドレスのドメインにはSPFレコードという「このサーバーからの送信を許可する」というDNS設定があります。A社のサーバーのアドレスを差出人にして、B社のサーバーから送ると、この設定と食い違って迷惑メール判定されやすくなります。

結局、メールアドレスを持っているサーバーと、中継スクリプトを置くサーバーを一致させるのが最もシンプルでした。DNSを触る必要もなく、テスト送信は迷惑メールフォルダではなく受信トレイに届きました。

おまけ:CLIは環境変数を平文で表示する

作業中にヒヤリとした話も残しておきます。

PaaSの管理画面では、環境変数の値は ******* と伏せられています。ところがコマンドラインツールで一覧を取得すると、値がそのまま出力されます。

「パスワードが設定されたか確認したい」だけのつもりが、APIキーや署名鍵まで含めて全部が画面に出てしまいました。

確認したいのは「設定されているか」であって「値が何か」ではないはずです。

# 悪い例:全部の値が表示される
$ cli variables

# 良い例:存在するかどうかだけ分かる
$ cli variables | grep -c "SMTP_PASSWORD"
1

秘密情報は、一度でもログや画面に出たら「漏れた前提」で作り直す。これが原則です。実際この件では、該当するキーをすべて再発行しました。地味ですが、事故になる前に気づけたのは幸いでした。

まとめ

  • PaaSはSMTPポートを塞いでいることが多い。設定を疑う前に、別のSMTPサーバーで再現するか試すと切り分けが早い
  • HTTPSは通るので、HTTPS経由でメールを送る手段を探せばよい
  • メール配信サービスは手軽だが、無料枠の日次上限は開発中のテストで埋まりやすい
  • すでに契約しているレンタルサーバーを中継役にすると、追加費用ゼロで上限も桁違いに大きい
  • 中継スクリプトには認証・差出人固定・レート制限を必ず実装する
  • 差出人ドメインのSPFと、実際に送信するサーバーを一致させる
  • 環境変数の確認は、値ではなく存在の有無だけを見る

「使っていないサーバーが手元にある」という状況は意外とよくあります。新しいサービスを契約する前に、手持ちの資産で解決できないか一度考えてみると、コストも依存先も減らせるかもしれません。

公開日 2026/09/04

関連記事