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

## **1. はじめに**
あるプロジェクトで、**クライアントのIPアドレスによって表示するページを切り替える** という要件がありました。  
最初は `request()->ip()` を利用してIPアドレスを判定しましたが、AWSの **Application Load Balancer（ALB）** を利用していたため、**想定外の動作** が発生しました。  

この記事では、**最初に実装したロジック、発生した問題、原因、そして解決策として採用したロジック** を紹介します。  

---

## **2. 最初の実装（失敗したロジック）**
最初の実装では、シンプルに `request()->ip()` を使って **アクセス元のIPを取得** し、特定のIPリストに含まれているかを判定して、表示するページを切り替えていました。

```php
<?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
<?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判定の状態をログに記録すると、よりトラブルシューティングしやすくなります。

```php
\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` の確認を忘れずに！**