2026年8月8日

2026年8月8日

PHPのメール送信:PHPMailerでSMTP送信する方法とSwiftMailerからSymfony Mailerへの移行

PHP からメールを送るなら、mail() 関数ではなく PHPMailer か Symfony Mailer を使い、外部の SMTP サーバーに認証付きで送るのが現在の基本です。かつて定番だった SwiftMailer は 2021 年にメンテナンスが終了しており、新しく使うライブラリではありません。この記事では、PHPMailer での SMTP 送信と日本語メールの設定、エラーの調べ方、SwiftMailer を使っている既存コードを Symfony Mailer に移す考え方、WordPress で SMTP を設定する正しい方法までを説明します。

mail()関数ではなくライブラリを使う理由

PHP の mail() 関数は、サーバーにある sendmail などのプログラムにメールを渡すだけの仕組みです。送信元ドメインの SPF や DKIM と合わない経路から送られることが多く、受信側で迷惑メール扱いになったり、届かなかったりする原因になります。共用サーバーやクラウドの VPS では、外向きの 25 番ポートが制限されていることもあります。

ライブラリを使って SMTP サーバー(自社のメールサーバーや、メール配信サービス)に認証付きで送ると、次の利点があります。

  • 送信元ドメインの SPF・DKIM・DMARC の設定と合わせやすく、到達率が上がりやすい
  • 送信に失敗したときに、SMTP サーバーからの応答でエラーの理由が分かる
  • HTML メール・添付ファイル・日本語の件名などを正しい形式で組み立ててくれる
  • 宛先やヘッダーの検証が入っているため、ヘッダーインジェクションを防ぎやすい

SwiftMailerの現状と選び方

SwiftMailer は 2021 年 11 月にメンテナンス終了が告知され、後継として Symfony Mailer(symfony/mailer)が案内されています。セキュリティ修正も提供されないため、既存のコードで使っている場合は移行を計画してください。

新規に選ぶなら、目安は次のとおりです。

  • フレームワークを使わない素の PHP や、小さな管理ツール:PHPMailer
  • Symfony や Laravel を使っている:フレームワークのメール機能(内部で Symfony Mailer を使う)
  • SwiftMailer からの移行:考え方が近い Symfony Mailer

どちらも Composer で導入します。各ライブラリの対応 PHP バージョンは更新で変わるため、導入前に公式リポジトリの説明を確認してください(2026年10月現在)。

PHPMailerでSMTP送信する

インストール

プロジェクトのディレクトリで次のコマンドを実行します。

composer require phpmailer/phpmailer

送信の基本コード

SMTP サーバーのホスト名・ポート・ユーザー名・パスワードは、利用するメールサーバーや配信サービスの案内に従います。パスワードをソースコードに直接書くと、Git などから漏れる原因になるため、環境変数や公開ディレクトリの外にある設定ファイルから読み込みます。

<?php
require __DIR__ . '/vendor/autoload.php';

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;

$mail = new PHPMailer( true ); // true で失敗時に例外を投げる

try {
	$mail->isSMTP();
	$mail->Host       = 'smtp.example.com';
	$mail->SMTPAuth   = true;
	$mail->Username   = getenv( 'SMTP_USER' );
	$mail->Password   = getenv( 'SMTP_PASS' );
	$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // 587番ポート
	$mail->Port       = 587;

	// 日本語のメール
	$mail->CharSet  = PHPMailer::CHARSET_UTF8;
	$mail->Encoding = PHPMailer::ENCODING_BASE64;

	$mail->setFrom( 'noreply@example.com', 'サンプル株式会社' );
	$mail->addAddress( 'user@example.net', '山田 太郎' );
	$mail->addReplyTo( 'support@example.com', 'サポート窓口' );

	$mail->isHTML( true );
	$mail->Subject = 'お問い合わせを受け付けました';
	$mail->Body    = '<p>お問い合わせありがとうございます。</p>';
	$mail->AltBody = 'お問い合わせありがとうございます。';

	$mail->send();
	echo '送信しました';
} catch ( Exception $e ) {
	error_log( 'Mail error: ' . $mail->ErrorInfo );
	echo '送信できませんでした。時間をおいて再度お試しください。';
}

ポートと暗号化の組み合わせは、587 番なら STARTTLS(ENCRYPTION_STARTTLS)、465 番なら最初から TLS で接続する SMTPS(ENCRYPTION_SMTPS)が一般的です。組み合わせを間違えると、接続はできても途中で止まる、タイムアウトするといった症状になります。

日本語の件名と本文

CharSet を UTF-8、Encoding を base64 にしておけば、件名・差出人名・本文の日本語は PHPMailer が適切にエンコードします。現在の主なメールソフトは UTF-8 を正しく表示できるため、ISO-2022-JP に変換する必要があるのは、古い携帯端末向けなど相手先の事情がある場合に限られます。

HTML メールには AltBody でテキスト版も入れておきます。テキスト版がないと、HTML を表示しない環境で本文が読めず、迷惑メール判定の材料になることもあります。

添付ファイル

addAttachment() にファイルのパスを渡すと添付できます。ユーザーがアップロードしたファイルを添付する場合は、ファイル名や拡張子を検証し、任意のパスを指定できないようにしてください。

$mail->addAttachment( '/var/app/storage/invoice-1001.pdf', 'ご請求書.pdf' );

送れないときの調べ方

$mail->ErrorInfo だけでは原因が分からないときは、SMTP のやり取りを記録します。

$mail->SMTPDebug   = SMTP::DEBUG_SERVER;
$mail->Debugoutput = 'error_log'; // 画面ではなくログへ出す

デバッグ出力には認証のやり取りが含まれ、パスワードを base64 にしただけの文字列が記録されることがあります。画面に表示しない、調査が終わったら SMTP::DEBUG_OFF に戻す、ログを残さない、の 3 点を守ってください。

よくある原因は、可能性の高い順に次のとおりです。

  1. 認証エラー(535 など):ユーザー名・パスワードの誤り、またはサービス側でアプリ用パスワードや API キーの利用が求められている。配信サービスの管理画面で認証方式を確認する
  2. 接続できない・タイムアウト:ポートがサーバー側のファイアウォールで閉じている。サーバーから openssl s_client -starttls smtp -connect smtp.example.com:587 で接続できるか試す
  3. 証明書の検証エラー:SMTP サーバーの証明書の期限切れ、またはサーバーの CA 証明書が古い。検証を無効にして回避するのではなく、証明書や OS の CA を更新する
  4. 送信元の拒否:setFrom() のアドレスが、SMTP サーバーで送信を許可されたドメインではない
  5. 送れたのに届かない:SPF・DKIM・DMARC の設定不足。受信したメールのヘッダーで認証結果を確認する

ヘッダーインジェクションを防ぐ

問い合わせフォームのように、外部からの入力をメールに使うときは次の点に注意します。

  • 送信者(From)には自分のドメインのアドレスを固定し、訪問者のアドレスは addReplyTo() に入れる。他人のドメインを From にすると、SPF や DMARC で拒否される
  • メールアドレスは PHPMailer::validateAddress() や filter_var( $email, FILTER_VALIDATE_EMAIL ) で検証する。addAddress() などは不正なアドレスで false を返すか、例外モードでは例外を投げる
  • 件名に入力値を使う場合は改行文字を取り除く
  • フォームには CSRF 対策のトークンと、連続送信を防ぐ回数制限を入れる

SwiftMailerからSymfony Mailerへ移行する

Symfony Mailer は、送信先を DSN という 1 行の文字列で指定し、メッセージを Email クラスで組み立てます。SwiftMailer の Swift_SmtpTransport が DSN に、Swift_Message が Email に、Swift_Mailer が Mailer に対応すると考えると移しやすいです。

composer require symfony/mailer
<?php
require __DIR__ . '/vendor/autoload.php';

use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mime\Email;

// 例: smtp://ユーザー名:パスワード@smtp.example.com:587
$transport = Transport::fromDsn( getenv( 'MAILER_DSN' ) );
$mailer    = new Mailer( $transport );

$email = ( new Email() )
	->from( 'noreply@example.com' )
	->to( 'user@example.net' )
	->replyTo( 'support@example.com' )
	->subject( 'お問い合わせを受け付けました' )
	->text( 'お問い合わせありがとうございます。' )
	->html( '<p>お問い合わせありがとうございます。</p>' );

try {
	$mailer->send( $email );
} catch ( TransportExceptionInterface $e ) {
	error_log( 'Mail error: ' . $e->getMessage() );
}

DSN にパスワードを入れるとき、@ や : などの記号は URL エンコードが必要です。これを忘れると、ホスト名の解釈がずれて接続先が変わり、原因の分かりにくいエラーになります。移行後は、日本語の件名・添付ファイル・複数宛先など、既存の送信パターンごとにテスト送信して表示を確かめてください。

WordPressではphpmailer_initでSMTPを設定する

WordPress の wp_mail() は内部で PHPMailer を使っています(WordPress 5.5 以降は PHPMailer\PHPMailer 名前空間の版を同梱)。そのため、プラグインやテーマで PHPMailer を別に読み込むと、同じクラス名の衝突や版の違いで不具合が起きることがあります。WordPress で SMTP を使うときは、phpmailer_init アクションで同梱の PHPMailer の設定を変えるのが正しい方法です。

<?php
// wp-content/mu-plugins/smtp-config.php
// 接続情報は wp-config.php に定数で定義しておく(例: define( 'MY_SMTP_USER', 'your-smtp-user' );)
add_action( 'phpmailer_init', function ( $phpmailer ) {
	if ( ! defined( 'MY_SMTP_USER' ) || ! defined( 'MY_SMTP_PASS' ) ) {
		return;
	}
	$phpmailer->isSMTP();
	$phpmailer->Host       = 'smtp.example.com';
	$phpmailer->SMTPAuth   = true;
	$phpmailer->Username   = MY_SMTP_USER;
	$phpmailer->Password   = MY_SMTP_PASS;
	$phpmailer->SMTPSecure = 'tls';
	$phpmailer->Port       = 587;
} );

送信に失敗したときは wp_mail_failed アクションで WP_Error を受け取ってログに残せます。コードを書かずに設定したい場合は、WP Mail SMTP などの SMTP 設定プラグインを使う方法もあります。どちらの場合も、本番で切り替える前にステージング環境でテスト送信し、届いたメールの認証結果を確認してください。

開発環境では、実際の宛先に送らずに送信内容を確認できる Mailpit のような受信用ツールを使うと、誤送信を防げます。

まとめ

  • mail() ではなく、PHPMailer や Symfony Mailer で SMTP サーバーに認証付きで送る
  • SwiftMailer はメンテナンス終了。既存のコードは Symfony Mailer への移行を計画する
  • 日本語は CharSet を UTF-8、Encoding を base64 にすれば正しく送れる
  • SMTP のパスワードはコードに書かず、デバッグ出力はログにだけ出して調査後に止める
  • From は自分のドメインに固定し、訪問者のアドレスは Reply-To に入れる
  • WordPress では PHPMailer を別に読み込まず、phpmailer_init で同梱版を設定する

お気軽にご相談ください

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