MXレコード検証:開発者向け完全ガイド
MXレコード検証:開発者向け完全ガイド Dec 13, 2025
メール検証のためのMXレコード検証の実装方法を学びます。この開発者ガイドでは、DNSルックアップ、MXレコードのパース、優先度の処理、メールドメイン検証のベストプラクティスについて解説します。 •
日本語 •
送信するすべてのメールは、緻密に組織化されたサーバーネットワークを経由して配信されます。Mail Exchange(MX)レコードは、この経路を案内する道標の役割を果たします。MXレコードの検証方法を理解することは、メール検証システム、コンタクトフォーム、またはメールアドレスを収集するアプリケーションを構築する開発者にとって必須のスキルです。この包括的なガイドでは、基本概念から高度な実装戦略まで、MXレコード検証について詳しく解説し、アプリケーションに堅牢なメール検証を組み込むための知識を提供します。
MXレコードの理解 Mail Exchangeレコードは、ドメインに代わってメールを受信するメールサーバーを指定するDNSレコードです。user@example.com にメールを送信すると、送信側のメールサーバーは配信先を知る必要があります。MXレコードは、ドメインのメールサーバーを指し示すことでこの情報を提供します。
MXレコードの仕組み メールが送信されると、送信側のメールサーバーは受信者のドメインのMXレコードを取得するためDNSルックアップを実行します。このルックアップは、優先順位を示す値とともに、1つ以上のメールサーバーのホスト名を返します。
gmail.com の典型的なMXレコードルックアップは以下のようになります:
gmail.com. MX 5 gmail-smtp-in.l.google.com.
gmail.com. MX 10 alt1.gmail-smtp-in.l.google.com.
gmail.com. MX 20 alt2.gmail-smtp-in.l.google.com.
gmail.com. MX 30 alt3.gmail-smtp-in.l.google.com.
gmail.com. MX 40 alt4.gmail-smtp-in.l.google.com.
送信サーバーは、最も優先度の低い(数値が小さい)サーバーから配信を試みます(この場合は優先度5)。そのサーバーが利用できない場合、次の優先度レベルを試みます。この冗長性により、個々のサーバーがダウンしていてもメール配信が確保されます。
MXレコードの構成要素 各MXレコードには2つの重要な情報が含まれています:
優先度(Preference)
メールサーバーを試行する順序を示す数値です。数値が小さいほど優先度が高くなります。同じ優先度のサーバーはランダムな順序で試行され、負荷分散が実現されます。
メールサーバーのホスト名
ドメインのメールを処理するメールサーバーの完全修飾ドメイン名(FQDN)です。このホスト名は、AレコードまたはAAAAレコードを介してIPアドレスに解決される必要があります。
メール検証におけるMXレコードの重要性 MXレコード検証は、メール検証プロセスにおいて重要なチェックポイントとして機能します:
ドメインの存在確認
ドメインにMXレコードがない場合、通常はメールを受信できません。一部のドメインにはAレコードのフォールバックがありますが、MXレコードがないことは、ドメインがメール用に設定されていない強い指標となります。
インフラストラクチャの検証
動作しているメールサーバーに解決される有効なMXレコードは、ドメインにメールインフラストラクチャが整っていることを示します。これは特定のアドレスが存在することを保証するものではありませんが、ドメインがメールを受信できることを確認します。
スパムと詐欺の検出
正規の企業は適切なMXレコードを維持しています。スパムや詐欺に使用される疑わしいドメインは、設定ミスや欠落したMXレコードを持つことがよくあります。
パフォーマンスの最適化
SMTP検証を試みる前にMXレコードをチェックすることで、メールを受信できないドメインへの接続に費やす時間を節約できます。
MXレコードルックアップの実装 さまざまなプログラミング環境でMXレコード検証を実装する方法を見ていきましょう。
Node.js 実装 Node.js は dns モジュールを通じて組み込みのDNS解決機能を提供します:
const dns = require('dns').promises;
async function getMxRecords(domain) {
try {
const records = await dns.resolveMx(domain);
// 優先度でソート(最小値を先頭に)
records.sort((a, b) => a.priority - b.priority);
return {
success: true,
domain,
records: records.map(r => ({
exchange: r.exchange,
priority: r.priority
}))
};
} catch (error) {
return {
success: false,
domain,
error: error.code,
message: getMxErrorMessage(error.code)
};
}
}
function getMxErrorMessage(code) {
const messages = {
'ENODATA': 'このドメインのMXレコードが見つかりません',
'ENOTFOUND': 'ドメインが存在しません',
'ETIMEOUT': 'DNSルックアップがタイムアウトしました',
'ESERVFAIL': 'DNSサーバーが応答に失敗しました'
};
return messages[code] || '不明なDNSエラー';
}
// 使用例
const result = await getMxRecords('gmail.com');
console.log(result);
メール検証のインサイト
今すぐ検証を開始 今日から BillionVerify でメール検証を開始しましょう。サインアップすると 100 個の無料クレジットが得られます。クレジットカード不要です。正確なメール検証で、メールマーケティングの ROI を向上させている何千もの企業に参加しましょう。
クレジットカード不要 · 毎日 100+ 無料クレジット · 30 秒で開始
Python 実装 Python の dnspython ライブラリの dns.resolver モジュールは、包括的なDNSルックアップ機能を提供します:
import dns.resolver
import dns.exception
def get_mx_records(domain):
try:
answers = dns.resolver.resolve(domain, 'MX')
records = []
for rdata in answers:
records.append({
'exchange': str(rdata.exchange).rstrip('.'),
'priority': rdata.preference
})
# 優先度でソート
records.sort(key=lambda x: x['priority'])
return {
'success': True,
'domain': domain,
'records': records
}
except dns.resolver.NXDOMAIN:
return {
'success': False,
'domain': domain,
'error': 'NXDOMAIN',
'message': 'ドメインが存在しません'
}
except dns.resolver.NoAnswer:
return {
'success': False,
'domain': domain,
'error': 'NoAnswer',
'message': 'このドメインのMXレコードが見つかりません'
}
except dns.exception.Timeout:
return {
'success': False,
'domain': domain,
'error': 'Timeout',
'message': 'DNSルックアップがタイムアウトしました'
}
# 使用例
result = get_mx_records('gmail.com')
print(result)
Go 実装 Go の net パッケージは、簡潔なDNSルックアップ関数を提供します:
package main
import (
"fmt"
"net"
"sort"
)
type MxResult struct {
Success bool
Domain string
Records []MxRecord
Error string
}
type MxRecord struct {
Exchange string
Priority uint16
}
func getMxRecords(domain string) MxResult {
records, err := net.LookupMX(domain)
if err != nil {
return MxResult{
Success: false,
Domain: domain,
Error: err.Error(),
}
}
if len(records) == 0 {
return MxResult{
Success: false,
Domain: domain,
Error: "No MX records found",
}
}
// 優先度でソート
sort.Slice(records, func(i, j int) bool {
return records[i].Pref < records[j].Pref
})
result := MxResult{
Success: true,
Domain: domain,
Records: make([]MxRecord, len(records)),
}
for i, r := range records {
result.Records[i] = MxRecord{
Exchange: r.Host,
Priority: r.Pref,
}
}
return result
}
func main() {
result := getMxRecords("gmail.com")
fmt.Printf("%+v\n", result)
}
高度なMX検証テクニック 基本的なMXルックアップはレコードの存在を確認しますが、包括的なメール検証にはより深い分析が必要です。
メールサーバー接続性の検証 MXレコードは、IPアドレスに解決される必要があるホスト名を指し示します。メールサーバーが実際に到達可能であることを検証します:
const dns = require('dns').promises;
const net = require('net');
async function validateMxConnectivity(domain) {
// MXレコードを取得
const mxResult = await getMxRecords(domain);
if (!mxResult.success) {
return mxResult;
}
// 各メールサーバーを検証
const validatedRecords = [];
for (const record of mxResult.records) {
const validation = await validateMailServer(record.exchange);
validatedRecords.push({
...record,
...validation
});
}
return {
success: true,
domain,
records: validatedRecords,
hasReachableServer: validatedRecords.some(r => r.reachable)
};
}
async function validateMailServer(hostname) {
try {
// ホスト名をIPに解決
const addresses = await dns.resolve4(hostname);
if (addresses.length === 0) {
return { reachable: false, error: 'Aレコードがありません' };
}
// ポート25への接続をテスト
const connected = await testConnection(addresses[0], 25);
return {
reachable: connected,
ip: addresses[0],
error: connected ? null : '接続が拒否されました'
};
} catch (error) {
return {
reachable: false,
error: error.message
};
}
}
function testConnection(host, port, timeout = 5000) {
return new Promise((resolve) => {
const socket = new net.Socket();
socket.setTimeout(timeout);
socket.on('connect', () => {
socket.destroy();
resolve(true);
});
socket.on('timeout', () => {
socket.destroy();
resolve(false);
});
socket.on('error', () => {
resolve(false);
});
socket.connect(port, host);
});
}
Aレコードフォールバックの処理 MXレコードが存在しない場合、メール標準(RFC 5321)では、ドメインのAレコードをフォールバックとして使用すべきと規定されています。検証にこのフォールバックを実装します:
async function getMailServers(domain) {
// まずMXレコードを試行
try {
const mxRecords = await dns.resolveMx(domain);
if (mxRecords.length > 0) {
return {
type: 'MX',
servers: mxRecords.sort((a, b) => a.priority - b.priority)
};
}
} catch (error) {
if (error.code !== 'ENODATA') {
throw error;
}
}
// Aレコードにフォールバック
try {
const aRecords = await dns.resolve4(domain);
if (aRecords.length > 0) {
return {
type: 'A_FALLBACK',
servers: [{ exchange: domain, priority: 0 }],
warning: 'Aレコードフォールバックを使用 - MXレコードが見つかりません'
};
}
} catch (error) {
if (error.code !== 'ENODATA') {
throw error;
}
}
return {
type: 'NONE',
servers: [],
error: 'ドメインのメールサーバーが見つかりません'
};
}
NullMXレコードの検出 RFC 7505 は、ドメインがメールを受け付けないことを明示的に示す「null MX」レコードを定義しています。これらのレコードには、優先度0と空のホスト名(".")を持つ単一のMXエントリがあります:
function hasNullMx(mxRecords) {
if (mxRecords.length === 1) {
const record = mxRecords[0];
if (record.priority === 0 &&
(record.exchange === '.' || record.exchange === '')) {
return true;
}
}
return false;
}
async function validateDomainMx(domain) {
const mxResult = await getMxRecords(domain);
if (!mxResult.success) {
return mxResult;
}
if (hasNullMx(mxResult.records)) {
return {
success: false,
domain,
error: 'NULL_MX',
message: 'ドメインは明示的にメールを受け付けません'
};
}
return mxResult;
}
MXルックアップのキャッシング DNSルックアップは検証のたびにレイテンシを追加します。パフォーマンス向上のためキャッシングを実装します:
class MxCache {
constructor(ttlMs = 3600000) { // デフォルトTTL 1時間
this.cache = new Map();
this.ttl = ttlMs;
}
get(domain) {
const entry = this.cache.get(domain.toLowerCase());
if (!entry) return null;
if (Date.now() > entry.expiry) {
this.cache.delete(domain.toLowerCase());
return null;
}
return entry.data;
}
set(domain, data) {
this.cache.set(domain.toLowerCase(), {
data,
expiry: Date.now() + this.ttl
});
}
// 利用可能な場合はDNS TTL値を尊重
setWithTtl(domain, data, ttlSeconds) {
const ttlMs = Math.min(ttlSeconds * 1000, this.ttl);
this.cache.set(domain.toLowerCase(), {
data,
expiry: Date.now() + ttlMs
});
}
}
const mxCache = new MxCache();
async function getMxRecordsCached(domain) {
const cached = mxCache.get(domain);
if (cached) {
return { ...cached, fromCache: true };
}
const result = await getMxRecords(domain);
if (result.success) {
mxCache.set(domain, result);
}
return { ...result, fromCache: false };
}
一般的なMXレコードパターン 一般的なMX設定を理解することで、検証結果の解釈と潜在的な問題の特定に役立ちます。
主要メールプロバイダー 主要プロバイダーのMXパターンを認識することで、無料メールアドレスを識別できます:
const knownProviders = {
'google': [
'gmail-smtp-in.l.google.com',
'googlemail-smtp-in.l.google.com',
'aspmx.l.google.com'
],
'microsoft': [
'outlook-com.olc.protection.outlook.com',
'mail.protection.outlook.com'
],
'yahoo': [
'mta5.am0.yahoodns.net',
'mta6.am0.yahoodns.net',
'mta7.am0.yahoodns.net'
],
'protonmail': [
'mail.protonmail.ch',
'mailsec.protonmail.ch'
]
};
function identifyEmailProvider(mxRecords) {
const exchanges = mxRecords.map(r => r.exchange.toLowerCase());
for (const [provider, patterns] of Object.entries(knownProviders)) {
for (const pattern of patterns) {
if (exchanges.some(ex => ex.includes(pattern.toLowerCase()))) {
return provider;
}
}
}
return 'unknown';
}
Google Workspace の検出 Google Workspace(旧G Suite)ドメインは Google のメールサーバーを使用しますが、無料メールアカウントではありません:
function isGoogleWorkspace(domain, mxRecords) {
const isGoogleMx = mxRecords.some(r =>
r.exchange.toLowerCase().includes('google') ||
r.exchange.toLowerCase().includes('googlemail')
);
// ドメインが既知のGoogleコンシューマードメインでないことを確認
const googleConsumerDomains = ['gmail.com', 'googlemail.com'];
const isConsumerDomain = googleConsumerDomains.includes(domain.toLowerCase());
return isGoogleMx && !isConsumerDomain;
}
自己ホスト型メールの検出 自社でメールをホストしているドメインは、サブドメインを指すMXレコードを持つことがよくあります:
function isSelfHosted(domain, mxRecords) {
const domainParts = domain.toLowerCase().split('.');
const baseDomain = domainParts.slice(-2).join('.');
return mxRecords.some(r => {
const exchange = r.exchange.toLowerCase();
return exchange.includes(baseDomain) &&
!isKnownProvider(exchange);
});
}
function isKnownProvider(exchange) {
const providers = ['google', 'microsoft', 'yahoo', 'outlook', 'protonmail'];
return providers.some(p => exchange.includes(p));
}
メール検証パイプラインにおけるMX検証 MX検証は、包括的なメール検証プロセスの1ステップです。その役割を理解することで、効果的な検証パイプラインを構築できます。
検証の順序 MX検証は通常、検証パイプラインの早い段階で実行されます:
async function verifyEmail(email) {
// 1. 構文検証(最速、ネットワーク不要)
const syntaxResult = validateEmailSyntax(email);
if (!syntaxResult.valid) {
return { valid: false, reason: 'invalid_syntax', details: syntaxResult };
}
const domain = email.split('@')[1];
// 2. MXレコード検証(高速DNSルックアップ)
const mxResult = await validateMxRecords(domain);
if (!mxResult.valid) {
return { valid: false, reason: 'no_mx_records', details: mxResult };
}
// 3. 追加チェック(使い捨て、役割ベースなど)
const domainCheck = await checkDomainReputation(domain);
if (domainCheck.isDisposable) {
return { valid: true, risky: true, reason: 'disposable_domain' };
}
// 4. SMTP検証(最も遅く、最も徹底的)
const smtpResult = await verifySmtp(email, mxResult.records);
return {
valid: smtpResult.exists,
deliverable: smtpResult.deliverable,
mxRecords: mxResult.records,
provider: mxResult.provider
};
}
並列MXルックアップ 複数のメールを検証する際は、異なるドメインのMXルックアップを並列化します:
async function verifyEmailsBatch(emails) {
// ドメインごとにメールをグループ化
const emailsByDomain = {};
for (const email of emails) {
const domain = email.split('@')[1];
if (!emailsByDomain[domain]) {
emailsByDomain[domain] = [];
}
emailsByDomain[domain].push(email);
}
// すべてのドメインのMXレコードを並列でルックアップ
const domains = Object.keys(emailsByDomain);
const mxResults = await Promise.all(
domains.map(domain => getMxRecordsCached(domain))
);
// 結果をドメインにマッピング
const mxByDomain = {};
domains.forEach((domain, index) => {
mxByDomain[domain] = mxResults[index];
});
// MXデータを使用してメールを処理
const results = [];
for (const email of emails) {
const domain = email.split('@')[1];
const mx = mxByDomain[domain];
if (!mx.success) {
results.push({ email, valid: false, reason: 'invalid_domain' });
continue;
}
// キャッシュされたMXデータを使用してSMTP検証を継続
const smtpResult = await verifySmtp(email, mx.records);
results.push({ email, ...smtpResult });
}
return results;
}
エラーハンドリングとエッジケース 堅牢なMX検証は、さまざまなエラー状態を適切に処理します。
DNSタイムアウトの処理 ネットワークの問題により、DNSルックアップがハングする可能性があります。タイムアウト処理を実装します:
async function getMxRecordsWithTimeout(domain, timeoutMs = 10000) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('DNS_TIMEOUT')), timeoutMs);
});
try {
const result = await Promise.race([
getMxRecords(domain),
timeoutPromise
]);
return result;
} catch (error) {
if (error.message === 'DNS_TIMEOUT') {
return {
success: false,
domain,
error: 'TIMEOUT',
message: 'DNSルックアップがタイムアウトしました',
retryable: true
};
}
throw error;
}
}
無効なドメインの処理 DNSルックアップを試みる前に、構文的に無効なドメインを処理します:
function isValidDomain(domain) {
if (!domain || typeof domain !== 'string') {
return false;
}
// 長さをチェック
if (domain.length > 253) {
return false;
}
// 有効な文字と構造をチェック
const domainRegex = /^(?!-)[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
if (!domainRegex.test(domain)) {
return false;
}
// 各ラベルの長さをチェック
const labels = domain.split('.');
for (const label of labels) {
if (label.length > 63) {
return false;
}
}
return true;
}
async function validateDomainMxSafe(domain) {
if (!isValidDomain(domain)) {
return {
success: false,
domain,
error: 'INVALID_DOMAIN',
message: 'ドメイン形式が無効です'
};
}
return await getMxRecordsWithTimeout(domain);
}
一時的なDNS障害の処理 DNS障害は一時的な場合があります。指数バックオフによる再試行ロジックを実装します:
async function getMxRecordsWithRetry(domain, maxRetries = 3) {
const delays = [1000, 2000, 4000];
for (let attempt = 0; attempt < maxRetries; attempt++) {
const result = await getMxRecordsWithTimeout(domain);
// 確定的な障害に対しては再試行しない
if (result.success ||
result.error === 'NXDOMAIN' ||
result.error === 'ENOTFOUND') {
return result;
}
// 一時的な障害に対して再試行
if (result.retryable && attempt < maxRetries - 1) {
await sleep(delays[attempt]);
continue;
}
return result;
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
セキュリティに関する考慮事項 MX検証は、開発者が対処すべきセキュリティ上の考慮事項を導入します。
DNSスプーフィング防止 標準的なDNSクエリは暗号化されておらず、スプーフィングに脆弱です。機密性の高いアプリケーションでは、DNS over HTTPS(DoH)の使用を検討してください:
const https = require('https');
async function getMxRecordsDoH(domain) {
const url = `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(domain)}&type=MX`;
return new Promise((resolve, reject) => {
https.get(url, {
headers: { 'Accept': 'application/dns-json' }
}, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
const response = JSON.parse(data);
if (response.Status !== 0) {
resolve({
success: false,
domain,
error: 'DNS_ERROR',
status: response.Status
});
return;
}
const records = (response.Answer || [])
.filter(a => a.type === 15)
.map(a => {
const [priority, exchange] = a.data.split(' ');
return {
priority: parseInt(priority),
exchange: exchange.replace(/\.$/, '')
};
})
.sort((a, b) => a.priority - b.priority);
resolve({
success: records.length > 0,
domain,
records
});
} catch (error) {
reject(error);
}
});
}).on('error', reject);
});
}
DNSクエリのレート制限 class DnsRateLimiter {
constructor(maxQueriesPerSecond = 100) {
this.tokens = maxQueriesPerSecond;
this.maxTokens = maxQueriesPerSecond;
this.lastRefill = Date.now();
}
async acquire() {
this.refillTokens();
if (this.tokens > 0) {
this.tokens--;
return true;
}
// トークンが利用可能になるまで待機
await sleep(1000 / this.maxTokens);
return this.acquire();
}
refillTokens() {
const now = Date.now();
const elapsed = now - this.lastRefill;
const tokensToAdd = (elapsed / 1000) * this.maxTokens;
this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
this.lastRefill = now;
}
}
const dnsLimiter = new DnsRateLimiter(50);
async function getMxRecordsRateLimited(domain) {
await dnsLimiter.acquire();
return getMxRecords(domain);
}
MX検証にBillionVerifyを使用する MX検証を自分で実装することは教育的価値がありますが、BillionVerifyのようなプロフェッショナルなメール検証サービスは、包括的なメール検証の一部としてMX検証を処理します。
メール検証APIを使用する利点 BillionVerifyのメール検証APIは、MX検証を構文チェック、SMTP検証、使い捨てメール検出などと組み合わせて、単一のAPI呼び出しで提供します。これにより、複数の検証システムを維持する必要がなくなります。
プロフェッショナルなサービスは、グローバルに分散されたDNSリゾルバを維持し、大規模なキャッシングを処理し、数百万の検証にわたってパフォーマンスを最適化します。
メールサーバーの設定は常に変化します。メール検証サービスは、既知のプロバイダー、使い捨てドメイン、メールサーバーパターンのデータベースを継続的に更新します。
API統合の例 async function verifyEmailWithBillionVerify(email) {
const response = await fetch('https://api.billionverify.com/v1/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BV_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});
const result = await response.json();
// MX情報はレスポンスに含まれます
console.log('MX有効:', result.mx_found);
console.log('ドメイン有効:', result.domain_valid);
console.log('配信可能:', result.is_deliverable);
return result;
}
まとめ MXレコード検証は、より多くのリソースを消費するチェックを試みる前に、ドメインがメールを受信できることを確認するメール検証の基本コンポーネントです。適切なMX検証を実装することで、無効なドメインを素早くフィルタリングし、検証のパフォーマンスを最適化し、より信頼性の高いメール処理アプリケーションを構築できます。
常にMXレコードをチェック - SMTP検証を試みる前にMXレコードをチェックし、時間とリソースを節約しますAレコードフォールバックを処理 - MXレコードのないドメインに対するRFC標準に従いますキャッシングを実装 - 繰り返しの検証におけるDNSルックアップのオーバーヘッドを削減します一般的なパターンを認識 - メールプロバイダーと潜在的なリスクを識別しますエラーを適切に処理 - タイムアウト、再試行、適切なエラーメッセージを実装しますカスタムメール検証システムを構築する場合でも、BillionVerifyのようなサービスと統合する場合でも、MXレコードを理解することで、アプリケーションに優れたメール処理を組み込むことができます。今すぐMX検証の実装を開始し、包括的なメール検証への第一歩を踏み出しましょう。
認証プロバイダーを選ぶ前に、精度と速度の面で BillionVerify と ZeroBounce を比較してみてください。