1. はじめに
あるプロジェクトで、クライアントのIPアドレスによって表示するページを切り替える という要件がありました。
最初は request()->ip()
を利用してIPアドレスを判定しましたが、AWSの Application Load Balancer(ALB) を利用していたため、想定外の動作 が発生しました。
この記事では、最初に実装したロジック、発生した問題、原因、そして解決策として採用したロジック を紹介します。
2. 最初の実装(失敗したロジック)
最初の実装では、シンプルに request()->ip()
を使って アクセス元のIPを取得 し、特定のIPリストに含まれているかを判定して、表示するページを切り替えていました。
<?php
$clientIp = request()->ip(); // クライアントのIPを取得
$allowedIps = [
'123.45.67.89', // 許可されたIP(仮)
'98.76.54.32',
'192.168.1.1'
];
$isAllowedIp = in_array($clientIp, $allowedIps);
$templatePath = $isAllowedIp ? 'test2025' : 'test2025_alt';
@include($templatePath . '.php');
?>
このロジックの意図
-
request()->ip()
で アクセス元のIPを取得 -
$allowedIps
のリストに 許可IPが含まれているか判定 - 許可されていれば
test2025
、そうでなければtest2025_alt
を表示
3. 発生した問題
このロジックでテストを行ったところ、想定通りにページが切り替わる場合と、そうでない場合がある という不安定な動作を確認しました。
具体的には:
- 特定のIPからのアクセスでも
test2025_alt
が表示される - 時間が経過すると、同じ端末からのアクセスでもページの切り替えが起こる
- 異なるネットワーク環境でアクセスしても、意図した動作にならないことがある
4. 原因の特定
調査の結果、AWSのApplication Load Balancer(ALB)を経由しているため、request()->ip()
では本来のクライアントIPが取得できていなかった ことが原因でした。
ALBの仕様
- ALBを通過すると、サーバー側(EC2など)が認識するのは ALB のIP になる
- 元のクライアントIPは
X-Forwarded-For
ヘッダーに含まれる - つまり、
request()->ip()
は ALBのIPを取得してしまう
具体例
あるクライアントが 192.168.10.100
からアクセスすると、ALBを経由して 次のようなヘッダー情報 が送信される:
X-Forwarded-For: 192.168.10.100, 172.31.0.5
この場合:
-
ALBのIP(
172.31.0.5
)がrequest()->ip()
の戻り値になってしまう -
本来取得すべきIP(
192.168.10.100
)がX-Forwarded-For
の最初の値として記録されている
5. 解決策
X-Forwarded-For
の最初のIPを取得する
ALB環境では、クライアントのオリジナルIPは X-Forwarded-For
の最初の値に入っている ため、それを取得するように修正しました。
<?php
// `X-Forwarded-For` を取得
$forwardedFor = request()->header('X-Forwarded-For');
// カンマ区切りの最初の値を取得(本来のクライアントIP)
$clientIp = $forwardedFor ? trim(explode(',', $forwardedFor)[0]) : request()->ip();
$allowedIps = [
'123.45.67.89', // 許可されたIP(仮)
'98.76.54.32',
'192.168.1.1'
];
// `X-Forwarded-For` に許可IPが含まれているかチェック
$isAllowedIp = false;
foreach ($allowedIps as $allowedIp) {
if (strpos($forwardedFor, $allowedIp) !== false || $clientIp === $allowedIp) {
$isAllowedIp = true;
break;
}
}
$templatePath = $isAllowedIp ? 'test2025' : 'test2025_alt';
@include($templatePath . '.php');
?>
6. 改善後の動作
この修正により:
✅ 本来のクライアントIPが正しく取得されるようになった
✅ ALB環境でも意図通りのページ切り替えが機能するようになった
✅ リクエストごとにIP判定が変わる問題が解消された
また、strpos()
を使って X-Forwarded-For
に許可IPが含まれているかチェック することで、リストのどこかにIPがある場合にも適切に判定できるようにしています。
7. まとめ
🔹 最初の問題
-
request()->ip()
を使っていたため、ALBを通過した際に ALBのIPを取得してしまい、正しいIP判定ができなかった - その結果、想定通りのページ切り替えができないケースが発生
🔹 原因
- ALBは元のクライアントIPを
X-Forwarded-For
に格納する仕様だった -
request()->ip()
では ALBのIPしか取得できなかった
🔹 解決策
X-Forwarded-For
の最初のIPを取得する処理を追加- 許可IPが
X-Forwarded-For
に含まれているかチェック - これにより、ALB環境でも安定したIP判定が可能になった
8. おまけ: デバッグ用ログ
実装後のデバッグ用に、IP判定の状態をログに記録すると、よりトラブルシューティングしやすくなります。
\Log::info('X-Forwarded-For: ' . ($forwardedFor ?: 'None'));
\Log::info('Client IP: ' . $clientIp);
\Log::info('Allowed IPs: ' . implode(',', $allowedIps));
\Log::info('Is Allowed: ' . ($isAllowedIp ? 'true' : 'false'));
\Log::info('Template Path: ' . $templatePath);
storage/logs/laravel.log
を確認することで、どのIPが取得され、どのページが選択されたかを検証 できます。
9. さいごに
ALB環境で request()->ip()
だけを信用すると正しいクライアントIPが取得できない という罠がありました。
X-Forwarded-For
を適切に処理することで、この問題を解決し、安定したページ切り替えが可能になりました! 🚀
ALBを使っている場合は、X-Forwarded-For
の確認を忘れずに!