モノレポ構成でつまづいたリバースプロキシとCORSの問題


## **1. はじめに**

モノレポ構成（フロントエンドとバックエンドを一つのリポジトリで管理する形）でアプリケーションを開発していたら、「APIとフロントエンドの連携がうまくいかない」という問題が発生しました。
検索してもうまく記事を見つけられず解決まで時間がかかったので、この記事が誰かの助けになれば嬉しいです。

## **2. 問題の現象**

### **2.1 発生した問題**
1. **メールリンクが動作しない**  
   - devise_token_authによるパスワードリセットメールに記載されたリンクをクリックするとHTTP 500エラーが発生。登録認証メールも同様。
2. **Cookieが正しく付与されない**  
   - 認証後にセッションが保持されず、パスワードリセット画面に遷移せず。（パスワードリセット画面はCookieにトークンが付与されていないと遷移できない設定にしていた）
3. **フロントエンドがAPIレスポンスを受け取れない**  
   - `/api/`経由でリクエストを送っているはずが、フロントエンドのHTMLが返される。

---

### **2.2 デバッグで判明したこと**
- **リバースプロキシ設定が不十分**  
  フロントエンド（`www.example.com`）からバックエンド（`api.example.com`）へのリクエストが正しくルーティングされていなかった。  
- **CORSやCookieの設定が複雑化**  
  不適切なCORS設定やリバースプロキシの欠如が原因で、APIへの通信が正常に行われていなかった。

## **3. 原因の分析**

今回の問題は主に以下の構成要因によるものでした：

1. **モノレポ構成によるドメイン間通信**  
   - フロントエンドとバックエンドが異なるドメイン（`www.example.com`と`api.example.com`）で動作。
   - ドメイン間通信にはCORSやCookieの正確な設定が必要。

2. **リバースプロキシ設定が未実装**  
   - フロントエンドが`/api/`リクエストをバックエンドに正しく転送していなかった。

3. **ヘルスチェック設定の見落とし**  
   - AWS ALB（ロードバランサー）のヘルスチェックパスが正しく設定されておらず、デプロイ時にロールバックが発生。

---

## **4. 解決方法**

### **4.1 リバースプロキシ設定の実装**
- **目的**  
  フロントエンドの`/api/`リクエストをバックエンドに正確に転送。  
- **解決策：Vercelの`vercel.json`ファイルに設定**
  ```json
  {
    "rewrites": [
      {
        "source": "/api/(.*)",
        "destination": "https://api.example.com/api/$1"
      },
      {
        "source": "/(.*)",
        "destination": "/index.html"
      }
    ]
  }
  ```
- **ポイント**  
  - `source`で`/api/`をバックエンドにリダイレクト。
  - 順序が重要。`/api/`ルートを最初に評価することで、APIリクエストが静的ファイルに吸収されないように。

---

### **4.2 CORS設定の最適化**
- **Rails APIでのCORS設定**
```ruby
  Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    if Rails.env.production?
      origins "https://api.example.com", 
              "https://www.example.com", 
              "https://example.vercel.app",
              /https:\/\/.*\.vercel\.app/ # Vercelのすべてのサブドメインを許可
    else
      origins "http://localhost:5173"  # 開発用
    end
    resource "*",
      headers: :any,
      methods: %i[get post put patch delete options head],
      credentials: true,
      expose: ["access-token", "expiry", "token-type", "uid", "client"]
  end
end
```
  
- **改善点**
  - 許可するドメイン（`www.example.com`と`api.example.com`、Vercelのサブドメイン）を設定。
  - Cookieを利用するため`credentials: true`を指定。

---

### **4.3 ALBのヘルスチェック設定**
- **ヘルスチェックパスを明確に**
  - ALBでの設定：
    ```
    パス: /health_check
    ポート: トラフィックポート
    成功コード: 200-399
    ```
  - Railsで以下の設定を追加：
    ```ruby
    config.host_authorization = { exclude: ->(request) { request.path == "/health_check" } }
    ```

---

## **5. 検証手順**

以下のコマンドで動作を確認：

1. **リバースプロキシの確認**
   ```bash
   curl -X GET -v https://www.example.com/api/v1/sessions
   ```
   - 正しいレスポンス（ステータス200）が返ることを確認。

2. **メールリンクの確認**
   - パスワードリセットメールから送信されるリンクをクリックして、期待通りのページが表示されることを確認。

## **6. 学んだこと**

1. **モノレポ構成の課題**
   - フロントエンドとバックエンドが分離した環境ではリバースプロキシ設定が重要。
2. **CORSの本番環境設定**
   - 開発環境で問題なく動いていても、本番環境用のCORSやCookie設定が必要。

## **7. リバースプロキシのまとめ**

リバースプロキシとは、クライアント（ユーザー）からのリクエストを受け取り、適切なサーバーに転送する役割を持つサーバーや仕組みのことです。

- **ロードバランシング**：複数のバックエンドサーバーにリクエストを分散させ、負荷を均等にします。  
- **セキュリティ強化**：バックエンドサーバーを直接公開せず、リバースプロキシを介してアクセスさせることで、セキュリティを向上。  
- **リクエストルーティング**：クライアントからのリクエストを適切なバックエンドサービスに振り分けます。  

今回のケースでは、Vercelのリバースプロキシ設定を使い、`/api/`に送られたリクエストをRails APIサーバーに転送するようにしました。これにより、フロントエンドとバックエンドの分離環境で通信が行えるようになりました。

もし同じような問題に直面している方がいたら、この記事が参考になれば幸いです。