PHP mail()
関数の使い方を徹底解説
Webアプリケーションにおいて、ユーザーへの通知、問い合わせフォームの送信、アカウント登録時の確認メールなど、メール送信機能は不可欠です。PHPでメールを送信する最も基本的な方法は、組み込み関数の mail()
を使用することです。この記事では、PHPの mail()
関数について、その基本的な使い方から、ヘッダーの詳細な操作、セキュリティ上の注意点、さらには現代的な開発における限界と代替手段に至るまで、約5000語にわたり徹底的に解説します。
はじめに:mail()
関数とは何か?
PHPの mail()
関数は、オペレーティングシステムが提供するメール送信プログラム(多くの場合は sendmail互換プログラム、または直接SMTPサーバー)を利用してメールを送信するためのインターフェースです。非常にシンプルにメールを送信できるため、小規模なアプリケーションや簡単な通知などでは手軽に利用されます。
しかし、mail()
関数はPHP自身が直接SMTPサーバーと通信してメールを送信するわけではありません。外部プログラムや設定に依存するため、環境によって挙動が異なる場合があります。また、SMTP認証のような高度な機能はサポートしていません。さらに重要なのは、mail()
関数はメールの送信を依頼するだけであり、相手にメールが届いたことを保証するものではないという点です。これらの限界についても後ほど詳しく触れます。
まずは、mail()
関数の基本的な使い方から見ていきましょう。
mail()
関数の基本
mail()
関数のシグネチャは以下のようになっています。
php
mail(
string $to,
string $subject,
string $message,
array|string $headers = [],
string $additional_params = ""
): bool
この関数は、メールの送信処理が正常にキューに入った場合(多くの場合はsendmailなどのプログラムに引き渡された場合)に true
を返し、そうでない場合に false
を返します。ただし、前述の通り true
が返されても、実際に受信者のメールボックスに届くことを意味するわけではありません。
引数は以下の通りです。
$to
(必須): 受信者のメールアドレスを指定します。複数の受信者に送る場合は、カンマ,
で区切って指定できます(例:"[email protected], [email protected]"
)。ただし、非常に多くの宛先に送信する場合は、後述するSMTPライブラリやメール送信サービスを検討すべきです。$subject
(必須): メール件名を指定します。日本語のようなマルチバイト文字を含む場合は、適切なエンコーディング処理が必要です。これについてはヘッダーの操作のセクションで詳しく解説します。$message
(必須): メールの本文を指定します。こちらも日本語のようなマルチバイト文字を含む場合は、適切なエンコーディング処理が必要です。HTMLメールやマルチパートメールを送信する場合も、ここで本文の内容を組み立てます。$headers
(省略可能): 追加のヘッダーを指定します。From
,Cc
,Bcc
,Content-Type
など、メールの様々なメタ情報を設定するために使われます。非常に重要な引数であり、後のセクションで詳細に解説します。省略した場合は、デフォルトのヘッダーが自動的に付加されますが、通常は明示的に設定する必要があります。配列または改行区切りの文字列で指定できます。$additional_params
(省略可能): コマンドラインオプションとしてメール送信プログラムに渡される追加のパラメータを指定します。最も一般的には、Return-Path (-f
オプション) を設定するために使用されます。これも後のセクションで詳しく解説します。
基本的なメール送信コード例
まずは、最小限の必須引数のみを使った最もシンプルなコード例です。
“`php
“`
この例では、$headers
と $additional_params
を省略しています。この場合、mail()
関数やsendmailなどのプログラムが自動的にヘッダーを生成します。しかし、通常は差出人アドレス (From
ヘッダー) などを明示的に指定しないと、受信者側で迷惑メールとして扱われたり、返信が正しく行えなかったりするため、実用的ではありません。
返り値の真偽について
mail()
関数が返す true
または false
は、あくまでPHPが外部のメール送信プログラム(例えば sendmail コマンド)を起動することに成功したか、またはPHP内部のSMTPモジュールに処理を引き渡せたか、といった送信処理の最初のステップが成功したかどうかを示しているに過ぎません。
true
が返されたとしても、以下のような理由でメールが相手に届かない可能性は十分にあります。
- sendmailなどのプログラムが実際にメールを送信できなかった(設定ミス、ネットワークエラー、SMTPサーバーのエラーなど)。
- 送信元サーバーのIPアドレスがブラックリストに登録されている。
- メールの内容やヘッダーがスパム判定された。
- 受信者のメールアドレスが存在しない。
- 受信者のメールボックスがいっぱいである。
- ファイアウォールやセキュリティソフトウェアによってブロックされた。
逆に false
が返された場合は、PHPがメール送信処理を開始できなかったことを意味します。これはPHPの設定ミス (sendmail_path
が正しくないなど) や、PHPから外部コマンドを実行する権限がない、メモリ不足などのPHP実行環境の問題である可能性が高いです。
したがって、mail()
の返り値だけでメールが正常に送信されたかどうかを判断することはできません。信頼性の高いメール配信が必要な場合は、後述するSMTPライブラリや外部メール送信サービスを検討すべきです。
ヘッダーの操作 ($headers
)
$headers
引数は mail()
関数で最も重要な引数の一つです。これにより、差出人、CC、BCC、返信先、メールの形式 (HTMLかプレーンテキストか)、エンコーディングなどを指定できます。
$headers
は、各ヘッダー行を改行コード (\r\n
) で区切った一つの文字列、またはヘッダー名をキー、ヘッダー値を値とする連想配列として指定できます。文字列で指定する場合が一般的ですが、PHP 8.0以降では配列での指定もサポートされています。配列で指定する方が、各ヘッダーを個別に管理しやすく、改行コードの挿入ミスなども防げます。
よく使われるヘッダー
ヘッダー名 | 説明 | 例 |
---|---|---|
From |
差出人のメールアドレスと名前を指定します。送信元を特定するために必須です。 | "From: Sender Name <[email protected]>" |
Reply-To |
受信者が返信した際の返信先アドレスを指定します。From と異なる場合に指定します。 |
"Reply-To: [email protected]" |
Cc |
カーボンコピーの受信者アドレスを指定します。複数指定する場合はカンマ区切り。 | "Cc: [email protected], [email protected]" |
Bcc |
ブラインドカーボンコピーの受信者アドレスを指定します。他の受信者には見えません。 | "Bcc: [email protected]" |
Subject |
件名を指定します。mail() 関数の $subject 引数で指定するため、通常ヘッダーには含めません。 |
mail() 関数の引数を使用 |
Date |
メールが作成された日時。通常は自動的に追加されます。 | 自動生成 |
Message-ID |
メールのユニークな識別子。通常は自動的に追加されます。 | 自動生成 |
MIME-Version |
MIME規格のバージョンを指定します。添付ファイルやHTMLメール、マルチパートメールで必須。 | "MIME-Version: 1.0" |
Content-Type |
メールの本文の形式 (プレーンテキスト, HTML, マルチパートなど) とエンコーディングを指定します。 | "Content-Type: text/plain; charset=UTF-8" または "text/html; ..." |
Content-Transfer-Encoding |
本文のエンコーディング方式を指定します。Base64やQuoted-Printableなど。 | "Content-Transfer-Encoding: 7bit" または "8bit" , "base64" など |
X-Mailer |
メール送信に使用したソフトウェア名を指定します。セキュリティ上の理由から非推奨の場合も。 | "X-Mailer: PHP/" . phpversion() |
複数のヘッダーを指定する方法 (文字列形式)
ヘッダーを文字列で指定する場合、各ヘッダー行は CRLF (\r\n
) で区切る必要があります。PHPの文字列内で改行を表す \n
だけでは不十分な環境もあるため、必ず \r\n
を使用するようにしましょう。
“`php
\r\n”;
$headers .= “Reply-To: [email protected]\r\n”;
$headers .= “Cc: [email protected]\r\n”;
$headers .= “Bcc: [email protected]\r\n”; // Bccは送信時には他の受信者に見えない
$headers .= “X-Mailer: PHP/” . phpversion(); // オプション
// mail() 関数の第4引数にヘッダー文字列を指定
$success = mail($to, $subject, $message, $headers);
if ($success) {
echo “メール送信キュー登録成功 (複数のヘッダー)”;
} else {
echo “メール送信キュー登録失敗 (複数のヘッダー)”;
}
?>
“`
注意点: Bccアドレスは、mail()
関数に渡す $headers
文字列には含めますが、最終的に受信者に届くメールのヘッダーには含まれません。sendmailなどのプログラムがBccアドレスを処理し、個別にメールを送信するためです。
複数のヘッダーを指定する方法 (配列形式 – PHP 8.0以降)
PHP 8.0以降では、ヘッダーを連想配列で指定できます。この方法では、改行コードを意識する必要がなく、より安全です。
“`php
“Sender Name
“Reply-To” => “[email protected]”,
“Cc” => “[email protected]”,
“Bcc” => “[email protected]”, // 配列でもBccは他の受信者に見えない
“X-Mailer” => “PHP/” . phpversion()
];
// mail() 関数の第4引数にヘッダー配列を指定
$success = mail($to, $subject, $message, $headers);
if ($success) {
echo “メール送信キュー登録成功 (配列ヘッダー)”;
} else {
echo “メール送信キュー登録失敗 (配列ヘッダー)”;
}
?>
“`
配列形式の方がコードが読みやすく、メンテナンスしやすいでしょう。特別な理由がない限り、PHP 8.0以降を使用している場合は配列形式をお勧めします。
日本語の件名とヘッダーエンコーディング (mb_encode_mimeheader
)
メールの件名や、From
, To
, Cc
などのヘッダーに含まれる名前フィールドに日本語のようなマルチバイト文字を使用する場合、そのまま送信すると文字化けの原因となります。これは、RFC (Request for Comments) で定義されているメールのヘッダーは基本的にASCII文字のみを想定しているためです。
マルチバイト文字を含むヘッダーフィールドは、MIME (Multipurpose Internet Mail Extensions) の仕様に基づいてエンコードする必要があります。このエンコーディングを行うのが、PHPの mb_encode_mimeheader()
関数です。
php
mb_encode_mimeheader(
string $string,
?string $charset = null, // 元文字列の文字エンコーディング (例: "UTF-8")
?string $encoding = null, // エンコード方式 (例: "B", "Q")
?string $separator = null, // エンコードされた文字列の区切り文字
int $max_len = 0 // 最大行長
): string|false
$string
: エンコードしたい文字列(例: 日本語の件名や名前)。$charset
:$string
の文字エンコーディング。通常は'UTF-8'
または'JIS'
を指定します。ウェブアプリケーションで扱う文字列は'UTF-8'
であることが多いでしょう。$encoding
: エンコーディング方式。'B'
は Base64、'Q'
は Quoted-Printable を表します。通常は'B'
(Base64) が使われます。$separator
: エンコードされた長い文字列が複数行に分割される場合の区切り文字。省略可能です。$max_len
: エンコードされた行の最大長。省略可能です。
例として、日本語の件名と差出人名を含むメールを送信する場合を考えます。
“`php
\r\n”; // またはmb_encode_mimeheaderの結果を直接使う
$headers .= “MIME-Version: 1.0\r\n”;
// 本文がUTF-8プレーンテキストの場合のContent-Type
$headers .= “Content-Type: text/plain; charset={$mail_charset}\r\n”;
$headers .= “Content-Transfer-Encoding: 7bit\r\n”; // ASCII文字のみの場合 (日本語含む場合は8bitやBase64)
// 日本語を含む本文 (UTF-8を想定)
$message_jp = “これは日本語の本文です。\n文字化けしていませんか?”;
// mail() 関数で送信
// 件名にはエンコードした文字列を、本文には元の文字列(必要ならmb_convert_encodingで変換)を使用
$success = mail($to, $encoded_subject, $message_jp, $headers);
if ($success) {
echo “日本語メール送信キュー登録成功”;
} else {
echo “日本語メール送信キュー登録失敗”;
}
?>
“`
重要: 上記の例では、From
ヘッダーの名前部分を手動で Base64 エンコードして mb_encode_mimeheader
と同じ形式で組み立てています。これは mb_encode_mimeheader
が文字列全体をエンコードするため、<[email protected]>
の部分もエンコードされてしまうのを避けるためです。mb_encode_mimeheader
は、件名のようにフィールド全体がエンコードされる場合に最も適しています。名前部分だけをエンコードしたい場合は、上記のように手動で組み立てるか、ライブラリを利用する方が確実です。
また、Content-Type
ヘッダーで本文の文字セット (charset
) を指定することも非常に重要です。ここで指定した文字セットで本文をエンコード(または元の文字列がその文字セットであること)する必要があります。
ヘッダーインジェクションのリスクと対策
ヘッダーインジェクションは、攻撃者がメールのヘッダーに不正な情報を挿入する攻撃手法です。特に、ユーザーからの入力値(例: 問い合わせフォームの「件名」「メールアドレス」フィールドなど)を mail()
関数の引数やヘッダー文字列に直接使用する場合に発生しやすくなります。
攻撃者は、入力フィールドに改行コード (\r\n
) と続けて新しいヘッダー行(例: Bcc: [email protected]
)を挿入します。これにより、意図しない宛先にメールが送信されたり、ヘッダーが改ざんされたりする可能性があります。悪用されると、ウェブサーバーが悪意のあるメールの送信元として利用され、踏み台にされてしまう危険があります。
例えば、問い合わせフォームの「件名」フィールドに、ユーザーが以下の文字列を入力したとします。
合法的な件名\r\nBcc: [email protected]
もしこの入力値をそのまま $subject
引数や $headers
文字列に使用すると、mail()
関数はこれを件名とBccヘッダーとして解釈してしまう可能性があります。(ただし、mail()
関数の $subject
引数や配列形式の $headers
では、内部的に改行コードが除去されるなどの対策が取られている場合もありますが、文字列形式の $headers
引数では非常に危険です)。
対策:
ヘッダーインジェクションを防ぐには、ユーザーからの入力値に改行コードが含まれていないことを確認することが最も重要です。入力値を使用する前に、以下のいずれかの方法で改行コードを除去または無効化する必要があります。
- 除去:
str_replace(["\r", "\n"], '', $input_string)
などで改行コードを取り除く。 - 無効化:
addcslashes($input_string, "\r\n")
などで改行コードをエスケープする。 - 検証:
strpos($input_string, "\r") === false && strpos($input_string, "\n") === false
などで改行コードが含まれていないかチェックし、含まれていればエラーとする。
入力値がメールアドレスの場合も同様に検証が必要です。filter_var($email, FILTER_VALIDATE_EMAIL)
を使ってメールアドレス形式として正しいかを検証し、さらに改行コードが含まれていないかチェックします。
コード例 (対策あり):
“`php
\r\n”;
$headers .= “Reply-To: ” . $from_email . “\r\n”; // 返信先をユーザーのアドレスにする場合
$headers .= “MIME-Version: 1.0\r\n”;
$headers .= “Content-Type: text/plain; charset=UTF-8\r\n”; // 本文がUTF-8プレーンテキストの場合
// 本文もユーザー入力を使用する場合は改行コード除去などの考慮が必要(ただし本文はヘッダーとは異なる構造)
$message = $_POST[‘message’] ?? ”;
// 本文内の改行コードは許可する場合が多いが、ヘッダーに影響しないよう注意
// — mail() 関数で送信 —
// 件名には改行除去後の文字列(日本語含むならmb_encode_mimeheaderも適用)を使用
// ここではsubject変数には既に改行除去済み&エンコード済み文字列が入っていると仮定
$final_subject = mb_encode_mimeheader($subject, $charset, “B”, “\r\n”);
$success = mail($to, $final_subject, $message, $headers);
if ($success) {
echo “メール送信キュー登録成功 (ヘッダーインジェクション対策済み)”;
} else {
echo “メール送信キュー登録失敗”;
}
?>
“`
ユーザー入力を扱うすべてのフィールドについて、必ず改行コードの除去または検証を行うように習慣づけましょう。配列形式の $headers
引数を使用する場合でも、各要素の値に含まれる改行コードは注意が必要です。
メッセージ本文の操作 ($message
)
$message
引数には、メールの本文を指定します。本文はプレーンテキストだけでなく、HTML形式にしたり、添付ファイルを付けたりすることもできます。ただし、添付ファイルや複雑な形式のメールを mail()
関数単体で扱うのは非常に手間がかかります。
プレーンテキストメール
最も基本的な形式です。$message
にテキスト文字列を指定し、Content-Type: text/plain
ヘッダーを指定します。
“`php
\r\n”;
$headers .= “Content-Type: text/plain; charset=UTF-8\r\n”; // 文字コードを指定
$headers .= “Content-Transfer-Encoding: 7bit\r\n”; // ASCIIのみの場合
$success = mail($to, $subject, $message, $headers);
// …
?>
“`
日本語を含む場合は、charset=UTF-8
のように適切な文字コードを指定し、本文の文字列もその文字コードになっている必要があります。必要に応じて mb_convert_encoding()
で変換します。
“`php
\r\n”; // From名前エンコード
$headers .= “MIME-Version: 1.0\r\n”;
$headers .= “Content-Type: text/plain; charset=UTF-8\r\n”;
$headers .= “Content-Transfer-Encoding: 8bit\r\n”; // 8bit文字を含む場合
$success = mail($to, $subject, $message_utf8, $headers);
// …
?>
``
Content-Transfer-Encodingに
8bitを指定することで、日本語のような8ビット文字をそのまま送信できます。ただし、すべてのメールサーバーやクライアントが
8bitエンコーディングを正しく処理できるわけではないため、より安全な
Quoted-Printableや
Base64を使用する場合もあります。しかし、プレーンテキストの場合は通常
7bitまたは
8bit` で十分です。
HTMLメール
メール本文をHTML形式で表示したい場合は、Content-Type
ヘッダーを text/html
に設定します。
“`php
‘;$message .= ‘
HTMLメールのテスト
‘;
$message .= ‘
これはHTML形式のメールです。
‘;
$message .= ‘
‘;
$message .= ‘