2026年6月16日

2026年6月16日

WordPressのメール送信を制限してスパム送信を防ぐ方法

はじめに

WordPressサイトが乗っ取られたり脆弱なプラグインを悪用されると、スパムメール送信の踏み台にされることがあります。IPがブラックリストに登録される前に、送信レート制限と監視を設定することが重要です。

症状・原因

  • サーバーのメールキューが大量のメールで溢れている
  • IPアドレスがスパムブラックリストに登録された
  • ホスティング会社からメール送信制限の警告が届いた
  • wp_mail() が予期せず大量呼び出しされている

解決手順

ステップ1:現在のメール送信状況を確認する

# Postfixのメールキューを確認
mailq | head -50
postqueue -p | grep -c "^[A-F0-9]"  # キュー内のメール数

# 送信元プロセスを確認
grep "from=<" /var/log/mail.log | tail -100 | awk '{print $7}' | sort | uniq -c | sort -rn

# WordPress から送信されているか確認
grep "wp-mail\|wp_mail\|phpmailer" /var/log/mail.log | tail -50

# 過去1時間の送信数
grep "$(date +'%b %d %H')" /var/log/mail.log | grep "status=sent" | wc -l

ステップ2:wp_mail にレート制限を実装する

// functions.php: wp_mail 送信レート制限

add_filter('wp_mail', function(array $args): array {
    $key       = 'mail_count_' . date('YmdH');
    $count     = (int) get_transient($key);
    $limit     = 50; // 1時間あたりの上限

    if ($count >= $limit) {
        // 上限超過:ログに記録してメールをブロック
        error_log(sprintf(
            '[WP Mail Limit] Blocked: to=%s subject=%s count=%d',
            is_array($args['to']) ? implode(',', $args['to']) : $args['to'],
            $args['subject'],
            $count
        ));
        // 空の受信者を返してメール送信をキャンセル
        $args['to'] = '';
        return $args;
    }

    set_transient($key, $count + 1, HOUR_IN_SECONDS);
    return $args;
});

// 送信失敗をログに記録
add_action('wp_mail_failed', function(WP_Error $error): void {
    error_log('[WP Mail Failed] ' . $error->get_error_message());
});

ステップ3:メール送信ログを実装する

// functions.php: 全メール送信をカスタムテーブルに記録

// テーブル作成(プラグイン有効化時または初回実行時)
function create_mail_log_table(): void {
    global $wpdb;
    $table = $wpdb->prefix . 'mail_log';
    if ($wpdb->get_var("SHOW TABLES LIKE '$table'") === $table) return;

    $wpdb->query("CREATE TABLE $table (
        id         BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        sent_at    DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        recipient  VARCHAR(255) NOT NULL,
        subject    VARCHAR(500) NOT NULL,
        status     VARCHAR(20)  NOT NULL DEFAULT 'sent',
        INDEX (sent_at)
    ) " . $wpdb->get_charset_collate());
}
add_action('init', 'create_mail_log_table');

// 送信成功を記録
add_filter('wp_mail', function(array $args): array {
    add_action('phpmailer_init', function() use ($args): void {
        global $wpdb;
        $wpdb->insert($wpdb->prefix . 'mail_log', [
            'recipient' => is_array($args['to']) ? implode(',', $args['to']) : $args['to'],
            'subject'   => $args['subject'],
            'status'    => 'sent',
        ]);
    });
    return $args;
});

// 直近100件のログを表示(管理画面ページに追加可能)
function get_recent_mail_log(int $limit = 100): array {
    global $wpdb;
    return $wpdb->get_results(
        $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}mail_log ORDER BY sent_at DESC LIMIT %d",
            $limit
        )
    );
}

ステップ4:外部SMTPに切り替えてスパム送認証を強化する

// functions.php: Gmail SMTP / SendGrid に切り替え
// 推奨プラグイン: WP Mail SMTP (wp plugin install wp-mail-smtp)

// または手動設定(SMTPクレデンシャルは wp-config.php に定義)
// wp-config.php に追加:
// define('SMTP_HOST', 'smtp.sendgrid.net');
// define('SMTP_PORT', 587);
// define('SMTP_USER', 'apikey');
// define('SMTP_PASS', 'your-sendgrid-api-key');

add_action('phpmailer_init', function(PHPMailer\PHPMailer\PHPMailer $phpmailer): void {
    if (!defined('SMTP_HOST')) return;

    $phpmailer->isSMTP();
    $phpmailer->Host       = SMTP_HOST;
    $phpmailer->SMTPAuth   = true;
    $phpmailer->Port       = SMTP_PORT;
    $phpmailer->Username   = SMTP_USER;
    $phpmailer->Password   = SMTP_PASS;
    $phpmailer->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
    $phpmailer->From       = get_option('admin_email');
    $phpmailer->FromName   = get_bloginfo('name');
});

ステップ5:異常な送信を検知してアラートを送る

// functions.php: 送信急増を検知して管理者に通知

add_action('wp_mail', function(): void {
    $key    = 'mail_spike_' . date('YmdHi'); // 1分単位
    $count  = (int) get_transient($key);
    $count++;
    set_transient($key, $count, MINUTE_IN_SECONDS * 2);

    if ($count === 10) { // 1分間に10通で警告
        $admin = get_option('admin_email');
        $msg   = sprintf(
            "サイト %s で1分間に%d通のメールが送信されました。\n送信元を確認してください。\n\n%s",
            home_url(),
            $count,
            date('Y-m-d H:i:s')
        );
        // wp_mail を使わず直接送信(ループ回避)
        mail($admin, '[警告] メール送信急増を検知', $msg);
    }
});

注意事項

  • wp_mail フィルターで $args['to'] を空にするとメール送信はキャンセルされますが、送信失敗エラーはログに出力されます
  • 外部SMTPを使用する場合、送信ドメインのSPF/DKIM/DMARCレコードをDNSに設定するとスパム判定を回避しやすくなります
  • メールログテーブルは定期的に古いレコードを削除する保守処理も実装してください(30〜90日保持推奨)

まとめ

メール送信制限は①mailq/var/log/mail.logで現状把握、②wp_mailフィルターでトランジェントを使った1時間あたり送信数カウント・上限超過で受信者を空にしてブロック、③カスタムテーブルへの全送信ログ記録、④SendGrid/GmailへのSMTP切り替えでSPF/DKIM認証を確立、⑤1分間に10通超えでmail()関数を直接使って管理者にアラートを送る仕組みで多層防御します。

お気軽にご相談ください

お見積りへ お問い合わせへ