1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWSのロードバランサーを意識しつつ、IPで表示するwebページを切り替え出来るようにした話。

Posted at

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のIP172.31.0.5)が request()->ip() の戻り値になってしまう
  • 本来取得すべきIP192.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 の確認を忘れずに!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?