当ページのリンクには一部広告が含まれています。
うさねこ気まぐれPG開発室

Contact Form 7で「問い合わせメールアドレスのドメイン存在チェック」を入れる方法(MX/A/AAAA対応)

本記事で紹介する「メールドメインのDNS存在チェック」は、Contact Form 7(CF7)の問い合わせフォームで、明らかなダミードメインを機械的に減らすための補助的な仕組みです(スパム対策の一部として使える場合があります)。
メールアドレスそのものの実在確認(いわゆる「メールアドレスの存在チェック」や個別のアカウント確認)を行うものではありません。また、DNS上でドメインが存在していても、受信側の設定や迷惑メール判定などにより、メールが届かない場合があります。
このため、本実装は「到達性の保証」ではなく、ノイズを減らすためのフィルターとして位置づけています(SMTP応答確認や実在アカウント確認は行いません)。

※ここで紹介するコードは学習・参考用のサンプルです。環境により動作が異なる場合があるため、本番導入前に必ずテスト環境で検証してください。

メールドメインのDNS存在チェック

ねこちゃん
ねこちゃん

また新しいコード書いたって?

うさちゃん
うさちゃん

メールの@以降をDNSで検証するだけの小物ツール。地味だけど効くやつ🐰💻

ねこちゃん
ねこちゃん

なるほど、ドメインだけ“存在するか”を先に見るんだね。


実装方針 取りこぼしを減らすDNSチェック設計

問い合わせフォームで、明らかなダミードメインを減らす補助策として、メールアドレスのドメインが“実在するか”をDNSで確認する方法があります。
aaaa@aaaacccan.com のような明らかに存在しないドメインを早い段階で弾けるため、フォームの負荷や迷惑メールを減らせる場合があります。

ここでは Contact Form 7(CF7)に対して、次の流れで実装します。

  • メールの @ 以降(ドメイン)を取り出す
  • DNSで MX(メール受信)を優先チェック
  • MXが無い場合もあるため、A / AAAA(DNS上で宛先が引ける)でもOK扱い
  • DNS結果は重いのでキャッシュする

という、現実運用で取りこぼしが少ない構成を紹介します。


なぜ「MXだけ」ではなく A/AAAA も見るの?

「メールの送受信」だけならMXが本筋…なんだけど、現場だと多くのドメインではMXを設定しますが、MXが未設定の場合に、環境や実装によって A/AAAA へフォールバックして扱われるケースもあるため、MXだけで即NGにすると取りこぼしが出る可能性があります。
そのため、MXだけで即NGにすると「実在するドメイン」を弾く可能性があるので、A/AAAAも併せて確認します。

そこで、

  • MXがあればOK
  • MXが無くても A/AAAA があれば「DNS上で応答があるドメイン」としてOK

にしておくと、日本企業の独自ドメインなども通しやすくなります。
※もちろん“メールが必ず届く保証”ではない。ここは後述

比較項目よくある実装今回の実装例
チェック対象MXレコードのみ
MXのみ判定(※取りこぼしが出る可能性)
MX → A / AAAA 対応
(MXがなくても A/AAAAが存在すれば「DNS的に実在するドメイン」とみなして通す 安全設計)
処理速度毎回DNSに問い合わせる
(フォーム送信ごとに遅延が出る場合がある。大量送信されるとDNS問い合わせが増え、サーバー負荷要因になり得る)
結果をキャッシュ(Transient)
(一度調べたドメインは一定時間WordPressに記憶させるため、2回目以降はDNS問い合わせを省けるため高速化しやすい
日本語ドメインエラーになる可能性大
日本語.jp などが入力されると、DNS関数が正しく動作せず誤判定の原因になる)
Punycode変換対応
xn--... 形式に変換してからDNSチェックを行うため、多言語ドメインも正しく判定)
汎用性フィールド名が固定
your-email 決め打ちなど、フォームの設定を変えると動かなくなる)
フィールド名を自動取得
(CF7のどのメールタグでも動作するように設計)

どう実装する?

今回の構成は次の通りです。

1) バリデーションのタイミング

CF7のメール入力(email / email*)のバリデーションにフックして、送信前に判定します。

  • 形式チェック(xxx@yyy)はCF7本体に任せる
  • こちらは「ドメインの存在」だけに集中

2) ドメイン抽出

入力されたメールアドレスから @ 以降を取り出して小文字化します。
日本語ドメイン(IDN)があり得るので、環境が対応していれば ASCII(punycode) に変換してからDNSチェックします。

3) DNSチェック(MX→A/AAAA)

DNS関数で、

  • MX がある → OK
  • MX がない → A または AAAA がある → OK
  • どれもない → NG

という判定。

4) DNS結果のキャッシュ

WordPressの Transient(set_transient / get_transient)で一定時間キャッシュします。

5) 失敗時のメッセージ

ここが大事で、「あなたのドメインは存在しません」みたいに断言しない
現実には企業セキュリティやDNS応答の事情で、正しいドメインでも引っかかる可能性がゼロではないから。

おすすめの言い方はこれ:

  • 「ドメインが確認できませんでした」
  • 「会社/プロバイダのセキュリティ設定により制限されている場合があります」
  • 「別のメールアドレスでもう一度お試しください」

実装サンプル ドメイン存在チェック部分

なぜMX以外も見るのか?

  • MXが未設定でも、環境や実装によってはA/AAAAを参照してメール配送が行われる場合があります。そのため、MXのみで即NGとすると実在するドメインを弾いてしまう可能性があるため、本実装ではA/AAAAの存在も確認しています。

キャッシュの重要性

  • DNS問い合わせ(checkdnsrr)は、外部ネットワークとの通信が発生するため、PHPの処理の中では比較的「重い」処理です。
  • 短時間に送信が集中すると、キャッシュがない場合はDNS問い合わせも増え、サーバー負荷要因になり得ます。また環境によっては、DNS側で制限がかかったり、失敗率が上がる可能性もあります。
  • WordPressの Transient(set_transient / get_transient)を使い、同一ドメインへのDNS問い合わせを一定時間キャッシュします。
  • (ログを併用する場合)ログにはメールアドレス等の個人情報が含まれ得るため、Webから直接参照できない場所に保存してください。
    可能であればwebroot外へ。難しい場合はサーバ設定(.htaccess / Nginx など)で外部アクセスを拒否してください。
/**
 * =========================================================
 * Contact Form 7: DNS check for email domain (MX -> A/AAAA)
 * - 目的:明らかなダミードメインを機械的に減らす(到達性保証ではない)
 * - 形式チェック(xxx@yyy)はCF7本体に任せ、ここでは「ドメイン存在」だけを見る
 * - 取りこぼしを減らすため MX が無くても A/AAAA があればOK扱い
 * - DNS問い合わせは重いので transient でキャッシュ
 *   ※OKは長め / NGは短め(DNS一時障害での誤ブロック事故を減らす)
 * =========================================================
 */

// 必須(email*)と任意(email)の両方のタグにフック
add_filter('wpcf7_validate_email',  'usaneko_cf7_validate_email_domain_dns', 10, 2);
add_filter('wpcf7_validate_email*', 'usaneko_cf7_validate_email_domain_dns', 10, 2);

/**
 * CF7 バリデーション本体
 */
function usaneko_cf7_validate_email_domain_dns($result, $tag) {

  // (任意)管理者のテスト送信は弾かない(※会員サイト等では運用に合わせて要調整)
  if ( current_user_can('manage_options') ) return $result;

  // タグ名取得(CF7のバージョン差:配列/オブジェクト両対応)
  $name = '';
  if ( is_object($tag) && isset($tag->name) ) {
    $name = (string) $tag->name;
  } elseif ( is_array($tag) && isset($tag['name']) ) {
    $name = (string) $tag['name'];
  }
  if ( $name === '' ) return $result;

  // POSTから取得(sanitize_emailで潰さず、rawからドメイン抽出する)
  $email_raw = isset($_POST[$name]) ? trim((string) wp_unslash($_POST[$name])) : '';
  if ( $email_raw === '' ) return $result;

  // ドメイン抽出(IDN→punycode対応)
  $domain = usaneko_cf7_extract_domain_from_email($email_raw);
  if ( $domain === '' ) return $result;

  // DNS存在チェック(MX → A/AAAA)
  // ※ネットワークやDNSフィルタ環境により、正しいドメインでも確認できない場合があります
  if ( ! usaneko_cf7_domain_dns_exists($domain) ) {

    // 断定しない文言(「存在しません」ではなく「確認できませんでした」)
    $result->invalidate(
      $tag,
      "メールアドレスのドメインを確認できませんでした。\n"
      . "入力内容をご確認のうえ、別のアドレスでもお試しください。"
    );
  }

  return $result;
}

/**
 * 補助:ドメイン抽出 + IDN(punycode)変換
 */
function usaneko_cf7_extract_domain_from_email($email) {

  $email = trim((string) $email);

  // @ が無い場合は終了(形式チェックは基本CF7に任せる)
  $pos = strrpos($email, '@');
  if ( $pos === false ) return '';

  $domain = strtolower(trim(substr($email, $pos + 1)));
  if ( $domain === '' ) return '';

  // 末尾ドット等のノイズ除去(例: example.com.)
  $domain = rtrim($domain, '.');

  // IDN(日本語ドメイン)→ Punycode
  if ( function_exists('idn_to_ascii') ) {
    $variant = defined('INTL_IDNA_VARIANT_UTS46') ? INTL_IDNA_VARIANT_UTS46 : 0;
    $ascii   = @idn_to_ascii($domain, 0, $variant);
    if ( is_string($ascii) && $ascii !== '' ) {
      $domain = $ascii;
    }
  }

  return $domain;
}

/**
 * 補助:DNSチェック(MX → A/AAAA)+ キャッシュ + ホワイトリスト
 */
function usaneko_cf7_domain_dns_exists($domain) {

  $domain = strtolower(trim((string) $domain));
  if ( $domain === '' ) return true;

  // 例外的に通したいドメイン(必要なら追加)
  $allow = array(
    // 'example.local',
    // 'intra.example',
  );
  if ( in_array($domain, $allow, true) ) return true;

  // キャッシュキー生成(衝突防止)
  $cache_key = 'usaneko_cf7_dns_' . md5($domain);
  $cached    = get_transient($cache_key);
  if ( $cached !== false ) {
    return ($cached === '1');
  }

  $ok = false;

  // 1) checkdnsrr が使えるなら最優先(速い)
  if ( function_exists('checkdnsrr') ) {
    if ( @checkdnsrr($domain, 'MX') ) {
      $ok = true;
    } elseif ( @checkdnsrr($domain, 'A') || @checkdnsrr($domain, 'AAAA') ) {
      $ok = true;
    }
  }

  // 2) 保険:dns_get_record(環境差があるので警告抑制)
  if ( ! $ok && function_exists('dns_get_record') ) {
    $mx = @dns_get_record($domain, DNS_MX);
    if ( ! empty($mx) ) {
      $ok = true;
    } else {
      $a = @dns_get_record($domain, DNS_A);
      if ( ! empty($a) ) {
        $ok = true;
      } else {
        $aaaa = @dns_get_record($domain, DNS_AAAA);
        if ( ! empty($aaaa) ) $ok = true;
      }
    }
  }

  // 3) それでも判断できない環境は「弾かない」保険(取りこぼし回避)
  // ※厳しくしたいなら true → false に変更
  if ( ! function_exists('checkdnsrr') && ! function_exists('dns_get_record') ) {
    $ok = true;
  }

  // キャッシュ:
  // - OK(存在する)は長め
  // - NG(存在しない)は短め(DNS一時障害での誤ブロックを引きずらない)
  $ttl = $ok ? 6 * 3600 : 10 * 60;
  set_transient($cache_key, $ok ? '1' : '0', $ttl);

  return $ok;
}

本チェックは“ドメインの存在確認”であり、メールが必ず届くことを保証するものではありません。


注意点

1) 届く保証はない

DNS上ドメインが存在しても、

  • 迷惑メール判定(SPF/DKIM/DMARC)
  • 受信側のフィルタ
  • 相手のサーバ都合

で届かないことはあります。

2) ブロック文言は断定しない

正しいドメインでもDNS応答が不安定な環境はあります。
だから「存在しません」ではなく「確認できませんでした」が安全。
※企業ネットワークやDNSフィルタ環境では、正しいドメインでも確認できない場合があります。

3) キャッシュは必須(DNSは重い)

フォームアクセスが増えるとDNS問い合わせが積み上がります。
Transient等でキャッシュするのは実用上ほぼ必須。

4) 例外的に通したいドメインが出る可能性

企業内の特殊ドメインやテスト環境など、運用していると「通らないけど正しい」ケースがゼロではないです。
その時は、許可リスト(ホワイトリスト)を追加できる設計にしておくと保守が楽です。

5) ログを取る場合は個人情報に注意

メールアドレス等をログに残す場合は、マスク(例:us***@example.com のように一部のみ残す)や保存期間(例:30日でローテ)を決め、閲覧権限を限定してください。


この記事のまとめ

  • CF7のスパム対策として「メールドメインのDNS存在チェック」は、明らかなダミードメイン除外に役立つ場合があります
  • MXレコードを優先し、MXが無い場合はA/AAAAも確認することで、取りこぼしを減らせます
  • DNS問い合わせは処理コストが高いため、実運用ではキャッシュの実装を強く推奨します
  • ブロック時の文言は断定せず、「確認できませんでした」などの柔らかい表現にします
  • これはメールの到達性を保証する仕組みではなく、明らかなダミードメインを除外するための対策です
  • DNS上で存在が確認できても、受信側の設定や迷惑メール判定などにより、メールが届かない場合もあります
  • このチェックはドメインの存在確認に限られるため、メールアドレス(ユーザー名部分)そのものが実在するかどうかまでは確認できません
  • 「完璧に防ぐ仕組み」ではなく「ノイズを減らすフィルター」的なものです

なお、本手法はスパムを“減らす”ための補助策であり、これ単体で完全に防げるものではありません。レート制限、ハニーポット、Turnstile/reCAPTCHA等と併用すると、全体のノイズ低減につながる場合があります。

WordPress+Cocoonを始めるなら「エックスサーバー」がおすすめ!

「これからWordPress+Cocoonでブログを作りたい!」という方には、
国内シェアNo.1の高速レンタルサーバー エックスサーバーがぴったりです。

  • 🚀 WordPressクイックスタート対応(最短10分でブログ開設)
  • 🪄 Cocoonテーマの自動インストールにも対応
  • 🔒 無料独自SSL/高セキュリティ/自動バックアップ完備
  • ☁️ 高速SSD・安定稼働率99.99%以上
  • 💰️ スタンダード 月額料金 990円~  693円~(キャンペーン中なら) 2025/11現在

「WordPress × Cocoon」を最もスムーズに始められる環境が整っています。
👉 今すぐチェック:レンタルサーバー エックスサーバー

最新のサービス価格は公式サイトでご確認ください。

免責事項

本記事のサンプルプログラムは動作・結果を保証するものではありません。 利用により発生したいかなるトラブル・損害についても、当方は責任を負いません。

広告