【PHP入門】最小構成で学ぶ!メールフォームの作り方(確認画面・セッション・CSRF対策)

Webサイトを運営していると、「お問い合わせフォーム」はほぼ必須の機能です。訪問者が気軽にコンタクトを取れる窓口であり、ビジネスチャンスにも繋がります。
この記事では、PHPを使って基本的なメールフォームを作成する方法を、最小限のコード構成でステップバイステップで解説します。
完成するメールフォームは、以下の3つのファイルで構成されます。
index.html(入力画面): ユーザーが名前やメールアドレスを入力するフォーム。confirm.php(確認画面): 入力内容を表示し、ユーザーに間違いがないか確認してもらう画面。send.php(送信・完了画面): 実際のメール送信処理と、送信完了メッセージを表示する画面。
この構成は、ユーザーが「入力内容を間違えたまま送信してしまう」のを防ぐための、最も一般的で親切な設計です。
1. 入力画面(index.html)の作成
まずは、ユーザーが情報を入力するためのHTMLフォームを作成します。これは特別なPHPの知識がなくても作成できます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>お問い合わせフォーム</title>
<!-- (中略) スタイルシート -->
</head>
<body>
<h2>お問い合わせフォーム</h2>
<form action="confirm.php" method="POST">
<div>
<label for="name">お名前:</label>
<input type="text" id="name" name="name" required>
</div>
<div>
<label for="email">メールアドレス:</label>
<input type="email" id="email" name="email" required>
</div>
<div>
<label for="message">お問い合わせ内容:</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<div>
<button type="submit">入力内容を確認する</button>
</div>
</form>
</body>
</html>
ポイント
<form action="confirm.php" method="POST">action="confirm.php": このフォームの送信先(データの送り先)がconfirm.phpであることを指定します。method="POST": データの送信方法を「POST」に指定します。POSTは、URLにデータが表示されず、送信できるデータ量にも実質制限がないため、フォーム送信で一般的に使われます。
<input name="name">name属性(name="name"やname="email")が非常に重要です。ここで指定した名前が、PHP側でデータを受け取る際の「キー」になります。
required- HTML5の機能で、この項目が未入力だとブラウザが警告を出し、フォームの送信を防いでくれます。簡易的なバリデーション(入力チェック)として便利です。
2. 確認画面(confirm.php)の作成
入力画面で送信されたデータをPHPで受け取り、表示する画面です。ここでは「セッション」と「セキュリティ対策」が重要なテーマになります。
<?php
// (1) セッションを開始
session_start();
// (2) POSTデータがない場合は入力画面に戻す
if ($_SERVER["REQUEST_METHOD"] !== "POST" || empty($_POST)) {
header("Location: index.html");
exit;
}
// (3) POSTデータをサニタイズしてセッションに保存
// (htmlspecialcharsはXSS対策の基本です)
$_SESSION['name'] = htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF-8');
$_SESSION['email'] = htmlspecialchars($_POST['email'], ENT_QUOTES, 'UTF-8');
$_SESSION['message'] = htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF-8');
// (4) CSRF対策のトークンを生成
if (!isset($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];
?>
<!DOCTYPE html>
<html lang="ja">
<!-- (中略) HTMLヘッドとスタイル -->
<body>
<h2>お問い合わせ内容の確認</h2>
<p>以下の内容でよろしければ「送信する」ボタンを押してください。</p>
<div class="confirmation">
<p><span>お名前:</span><br>
<!-- (5) セッションからデータを表示 -->
<?php echo nl2br($_SESSION['name']); ?>
</p>
<p><span>メールアドレス:</span><br>
<?php echo nl2br($_SESSION['email']); ?>
</p>
<p><span>お問い合わせ内容:</span><br>
<?php echo nl2br($_SESSION['message']); // nl2brで改行を<br>タグに変換 ?>
</p>
</div>
<div class="button-group">
<!-- (6) 修正するボタン -->
<a href="javascript:history.back()" class="back-button">修正する</a>
<!-- (7) 送信フォーム -->
<form action="send.php" method="POST">
<!-- CSRFトークンを送信 -->
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>">
<button type="submit">送信する</button>
</form>
</div>
</body>
</html>
ポイント解説
(1) session_start(); PHPでセッション機能を使うための宣言です。セッションとは、複数のページをまたいでユーザーのデータ(訪問回数やログイン状態、フォームの入力内容など)を一時的にサーバー上に保存しておく仕組みです。 必ず、HTMLの出力よりも前に呼び出す必要があります。
(2) 不正アクセスの防止 confirm.php は index.html からPOST送信されることを前提としています。もしブラウザで直接 confirm.php にアクセスされた場合(POSTデータがない場合)は、index.html に強制的に戻します。
(3) データの受け取りとXSS対策
$_POST['name']のように、$_POSTというPHPのグローバル変数で、index.htmlのname属性をキーにしてデータを受け取れます。htmlspecialchars(): これは非常に重要な関数で、「クロスサイトスクリプティング(XSS)」という攻撃を防ぎます。- もし攻撃者がお問い合わせ内容に
<script>alert('攻撃!');</script>のような悪意のあるコードを入力した場合、htmlspecialcharsを使わずにそのまま表示すると、そのスクリプトが実行されてしまいます。 - この関数を通すことで、
<や>といった記号を<や>のような無害なHTMLエンティティに変換し、スクリプトの実行を防ぎます。
- もし攻撃者がお問い合わせ内容に
$_SESSION['name'] = ...: 受け取ったデータを、セッション変数($_SESSION)に保存しています。これにより、次のsend.phpでもこのデータを参照できます。
(4) CSRF対策
- 「クロスサイト・リクエスト・フォージェリ(CSRF)」という攻撃を防ぐための対策です。
bin2hex(random_bytes(32))でランダムな文字列(トークン)を生成し、セッションに保存します。- このトークンを、次の送信フォームに
input type="hidden"で埋め込みます((7)参照)。 send.phpでは、「セッションに保存されたトークン」と「フォームから送信されたトークン」が一致するかをチェックします。これにより、「正規の確認画面を経由して送信されたリクエストであること」を証明できます。
(5) データの表示と nl2br()
$_SESSIONに保存したデータをechoで表示します。nl2br()は、改行文字(\n)をHTMLの<br>タグに変換する関数です。textareaで入力された改行を、HTML上で正しく表示するために使います。
(6) 「修正する」ボタン
javascript:history.back()は、ブラウザの「戻る」ボタンと同じ機能です。入力画面に戻り、内容を修正できるようにします。
(7) 「送信する」フォーム
- 次の
send.phpへデータを送るためのフォームです。 - このフォームにはユーザーが入力する項目はありませんが、(4)で生成したCSRFトークンを
type="hidden"でこっそり含めて送信しています。
3. 送信・完了画面(send.php)の作成

確認画面で「送信する」ボタンが押されたときに、実際にメールを送信し、ユーザーに完了メッセージを伝える画面です。
<?php
// (1) セッションを開始
session_start();
// (2) CSRFトークンの検証
if ($_SERVER["REQUEST_METHOD"] !== "POST" || !isset($_POST['token']) || $_POST['token'] !== $_SESSION['token']) {
echo "不正なリクエストです。";
// (中略) セッション破棄
exit;
}
// (3) セッションからデータを取得
if (!isset($_SESSION['name']) || !isset($_SESSION['email']) || !isset($_SESSION['message'])) {
header("Location: index.html");
exit;
}
$name = $_SESSION['name'];
$email = $_SESSION['email'];
$message = $_SESSION['message'];
// (4) メール送信処理
// ★★★ ここはあなたのメールアドレスに変更してください ★★★
$to = "your-email@example.com";
$subject = "お問い合わせフォームからのメッセージ";
$body = "お問い合わせフォームから以下のメッセージが届きました。\n\n";
$body .= "=================\n";
$body .= "お名前: " . $name . "\n";
$body .= "メールアドレス: " . $email . "\n";
$body .= "お問い合わせ内容:\n";
$body .= $message . "\n";
$body .= "=================\n";
// (5) メールのヘッダー
$headers = "From: " . $email . "\r\n";
$headers .= "Reply-To: " . $email . "\r\n";
$headers .= "X-Mailer: PHP/" . phpversion();
// (6) 日本語メール送信のための設定
mb_language("Japanese");
mb_internal_encoding("UTF-8");
// (7) メール送信
$send_result = mb_send_mail($to, $subject, $body, $headers);
// (8) 送信結果のメッセージ
if ($send_result) {
$result_message = "お問い合わせいただきありがとうございます。メッセージは正常に送信されました。";
} else {
$result_message = "メッセージの送信に失敗しました。恐れ入りますが、時間をおいて再度お試しください。";
}
// (9) セッションデータを破棄(二重送信防止)
$_SESSION = [];
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
?>
<!DOCTYPE html>
<html lang="ja">
<!-- (中略) HTMLヘッドとスタイル -->
<body>
<h2>送信完了</h2>
<div class="result-message">
<p><?php echo htmlspecialchars($result_message, ENT_QUOTES, 'UTF-8'); ?></p>
</div>
<a href="index.html" class="back-link">フォームのトップに戻る</a>
</body>
</html>
ポイント解説
(1) session_start(); confirm.php で保存したセッションデータを読み込むため、ここでも最初に呼び出します。
(2) CSRFトークンの検証 confirm.php から送られてきた $_POST['token'] と、セッションに保存されている $_SESSION['token'] を比較します。これらが一致しない(または存在しない)場合は、不正なリクエストとみなし、処理を中断します。
(3) セッションからデータを取得 $_SESSION に保存されていた name や email の情報を、ローカル変数($name, $email など)に代入します。この時点でセッションデータが何らかの理由で消えていた場合は、入力画面に戻します。
(4) メール本文の作成 $body という変数に、メールに書きたい内容を文字列として連結していきます。\n は改行を意味します。
(5) メールのヘッダー
$headersには、メールの補助情報を設定します。From: " . $email: 送信元(From)を、フォームに入力されたメールアドレスに設定します。これにより、メールソフトで受信した際に、誰からの問い合わせかが分かりやすくなります。Reply-To: " . $email: 返信先(Reply-To)も設定しておくと、メールソフトの「返信」ボタンを押したときに、自動でユーザーのメールアドレスが宛先に入り便利です。
(6) 日本語メール設定 mb_send_mail で日本語(マルチバイト文字)を正しく送信するためのおまじないです。文字化けを防ぐために設定します。
(7) メール送信 mb_send_mail($to, $subject, $body, $headers); この関数が、実際にメールを送信する処理を行います。送信に成功すれば true、失敗すれば false を返します。
(8) 送信結果のメッセージ $send_result の結果(trueかfalseか)を見て、ユーザーに表示するメッセージを切り替えます。
(9) セッションデータの破棄 非常に重要です。 メール送信が完了(成功・失敗に関わらず)したら、セッションに保存したフォームデータとトークンを破棄します。
$_SESSION = [];でセッション変数を空にします。session_destroy();でセッション自体を完全に破棄します。- もしこれを忘れると、ユーザーが送信完了画面でブラウザの「更新」ボタンを押したときに、
send.phpが再度実行され、同じメールが何度も送信されてしまう(二重送信)可能性があります。
4. 動作確認と注意点
(1) 宛先メールアドレスの変更
send.php の (4) の部分にある $to = "your-email@example.com"; を、必ずあなたが受信できるメールアドレスに変更してください。
(2) サーバー環境
このコードは、PHPが動作するサーバー(XAMPP, MAMP, レンタルサーバーなど)にアップロードしないと動作しません。
(3) メールが送信されない場合
レンタルサーバーなどでは、迷惑メール対策としてPHPからのメール送信に制限がかかっていたり、別途設定が必要だったりする場合があります。 また、ローカル環境(XAMPPなど)からメールを送信するには、PC自体にメール送信サーバー(SMTPサーバー)の設定が必要となり、ハードルが上がります。
テストがうまくいかない場合は、まず send.php の if ($send_result) の分岐を強制的に if (true) にしてみて、完了画面が表示されるか(処理が最後まで通るか)を確認するのも一つの手です。
5. 次のステップ(機能向上)
この記事のコードは、学習のための最小構成です。実際の運用では、以下の点を改良することを強く推奨します。
- 入力値のバリデーション強化:
confirm.phpの冒頭で、$_POSTの内容を厳しくチェックします。- 「お名前」が空でないか?(
requiredだけでは不十分な場合もある) - 「メールアドレス」が正しい形式か?(
filter_var($email, FILTER_VALIDATE_EMAIL)などを使う) - 「お問い合わせ内容」が長すぎないか?
- スパム対策:
- このまま公開すると、海外のボット(プログラム)から大量のスパムメールが送られてくる可能性があります。
- Googleの「reCAPTCHA」などを導入するのが一般的です。
- 自動返信メール(サンキューメール):
send.phpで管理者宛のメールを送信した後、入力されたメールアドレス($email)宛にも「お問い合わせを受け付けました」という内容のメールを別途送信すると、ユーザーが安心できます。
まとめ
PHPでメールフォームを作る流れを解説しました。 一見難しそうに見えますが、「HTMLでデータを受け取り」「PHPで処理してメールを送る」という流れが基本です。
重要なのは、htmlspecialchars(XSS対策)や トークン(CSRF対策)、セッション破棄(二重送信防止)といったセキュリティやユーザビリティへの配慮です。
この記事のコードをベースに、ぜひ自分だけのメールフォームを作成してみてください。
