はじめに: CLBとGCSの落とし穴

Google Cloudで静的サイトをホスティングする際、CLB (グローバル外部アプリケーション ロードバランサ)Cloud Storage (GCS) バックエンドの組み合わせは、セキュリティとパフォーマンスを両立する理想的な構成です。

CLB + GCSの構築の仕方はこちら

しかし、

一般的なWebサーバーと異なり、CLBはルートURL(https://domain.com/)へのアクセスを自動で /index.html として処理してくれません。

なのでルートURL(https://domain.com/)にアクセスしてもxmlが表示されてしまいます。

この問題を解決するために、リダイレクトとリライトの2つのアプローチを比較し、最適な設定を解説します。

目標

ルートURL(https://domain.com/)にアクセスしindex.htmlを表示させつつ
さらにURLもルートURL(https://domain.com/)のままにする
※https://domain.com/index.htmlにならないようにする

前提


ルートについてはGoogle Cloudコンソールから変更はできるものの、
細かな設定ができないのでyamlファイルを作成してCLIから操作します。
※/と/index.htmlの関係性をうまく定義できないと無限リダイレクトが発生してしまいました。

失敗アプローチ – リダイレクトによる実現

最初に試したのは、fullPathMatch: / で厳密にルートパスのみをマッチさせ、/index.html へ301リダイレクトする方法でした。

# step1_redirect_with_fullpath.yaml
name: my-url-map
defaultService: projects/my-project/global/backendServices/my-backend-bucket

hostRules:
  - hosts:
      - '*' 
    pathMatcher: matcher1

pathMatchers:
  - name: matcher1
    routeRules:
      - matchRules:
          - fullPathMatch: /
        priority: 1
        urlRedirect:
          pathRedirect: /index.html
          redirectResponseCode: MOVED_PERMANENTLY_DEFAULT
          stripQuery: false
    defaultService: projects/my-project/global/backendServices/my-backend-bucket

結果

  • index.htmlが表示されるようにはなった
  • URLバーが /index.html に変わってしまう
  • 目標である「URLを変えずに表示」を達成できない

ちなみに
prefixMatch に変更してテストもしてみました。

# step2_redirect_with_prefix.yaml
name: my-url-map
defaultService: projects/my-project/global/backendServices/my-backend-bucket

hostRules:
  - hosts:
      - '*' 
    pathMatcher: matcher1

pathMatchers:
  - name: matcher1
    routeRules:
      - matchRules:
          - prefixMatch: / 
        priority: 1
        urlRedirect: 
          pathRedirect: /index.html 
          redirectResponseCode: MOVED_PERMANENTLY_DEFAULT
          stripQuery: false
    defaultService: projects/my-project/global/backendServices/my-backend-bucket

結果

現象変わらず、リダイレクトではうまくいかなそう

成功アプローチ – URLリライトによる実現

リダイレクト (urlRedirect) は、ブラウザに「別のURLに移動せよ」と指示するため、URLバーの表示が /index.html に変わってしまい、今回の要件には適さないことが判明しました。

解決策:URLリライト (urlRewrite)

CLBがリクエストを内部で書き換えてバックエンドに転送する URLリライト に切り替えました。

# step3_rewrite_final.yaml - 最終設計
name: my-url-map
defaultService: projects/my-project/global/backendServices/my-backend-bucket

hostRules:
  - hosts:
      - '*' 
    pathMatcher: matcher1

pathMatchers:
  - name: matcher1
    # --- [ ルートパス / から /index.html への URL 書き換えとルーティング ] ---
    routeRules:
      - matchRules:
          # ★ 厳密にルートパス / のみに一致させる
          - fullPathMatch: /
        priority: 1
        # ★ リダイレクトの代わりにルーティングアクションを設定
        routeAction:
          # パスを /index.html に内部的に書き換え
          urlRewrite:
            pathPrefixRewrite: /index.html
          # 書き換え後のリクエストをバックエンドサービスに転送
          weightedBackendServices:
            - backendService: projects/my-project/global/backendServices/my-backend-bucket
              weight: 100

    # --- [ ルートパス以外の、その他の全てのパスの処理 ] ---
    # /index.html や /images/ など、その他の全てのパスの処理は、
    # このデフォルトサービスが引き続き担当します。
    defaultService: projects/my-project/global/backendServices/my-backend-bucket

結果

みごとルートURL(https://domain.com/)のままindex.htmlの内容が表示されるようになりました。

各種パラメーターの説明

設定要素 役割 透過性の実現
fullPathMatch: / / のみに厳密に一致 /index.html へのループを完全に回避
routeAction CLBが転送処理を実行 ブラウザへのリダイレクト(301)を停止
pathPrefixRewrite: /index.html CLBはバックエンドにリクエストを送る際、パスを /index.html に書き換え ユーザーのURLバーは / のまま変わらず、コンテンツが表示される
weightedBackendServices 書き換え後のリクエストを明示的にバックエンドに転送 確実なルーティング
defaultService その他のすべてのパスを処理 リライト後の /index.html や、/images/ など、その他の全てのファイルリクエストを正常に処理

まとめ

Google Cloud CLBでルートパスを透過的に解決するには、URLリライトで実現することができました。

アプローチ URLバーの変化 最適な利用シーン
リダイレクト (urlRedirect) ❌ 変わる(/index.html になる) URLを永続的に変更したい場合
リライト (urlRewrite) ✅ 変わらない(/ のまま) 静的サイトのルートファイル解決(本件の最適解)

参考コマンド

# バックエンドサービスの確認
gcloud compute backend-services describe my-backend-bucket --global
gcloud compute backend-services list --global

# URL Mapの更新
gcloud compute url-maps import my-url-map \
  --source=step3_rewrite_final.yaml \
  --global

# 設定の確認
gcloud compute url-maps describe my-url-map --global

この設定により、CLBのセキュリティを担保しながら静的サイトホスティングを実現させることができます。