Cloudflare Pages Functionsによるエッジサイドi18nリダイレクトの仕組み。Cookie、Accept-Language、国情報を優先順位として判定し、ユーザーを適切な言語サイト(英語/日本語)へ誘導しつつ、BotのSEOも維持するフロー図。

背景

多言語Webサイトの課題は、ユーザーの言語設定を自動判定し、最適な言語バージョンへシームレスにリダイレクトすることです。

VitePressで構築した多言語サイト(英語 / と日本語 /ja/)において、従来はユーザーが手動で言語を切り替える必要がありました。初回訪問時にAccept-Languageヘッダーから自動判定し、Cookieで言語選択を永続化することで、UXを大幅に改善できます。

エッジサイド処理(Cloudflare Pages Functions) を活用することで、以下のメリットが得られます:

  • 低レイテンシ: オリジンサーバーへのリクエスト前に言語判定が完了
  • 高パフォーマンス: CDNレベルでのリダイレクト処理で不要なオリジンリクエストを削減
  • SEO維持: ボット検出により検索エンジンクローラーにはリダイレクトしない

本ガイドでは、このEdge-side i18n実装を段階的に解説します。

こんな人におすすめ

  • Cloudflare Pages/Workersで多言語対応を考えている方
  • エッジサイドでのリダイレクト実装に興味のある方
  • VitePressで国際化対応に取り組んでいる方
  • セキュアなCookie実装やボット検出について学びたい方
  • Edge FunctionsとAccept-Languageの統合方法を知りたい方

目次

よくある質問(FAQ)

Q: Edge Functions を使う理由は何か?

A: エッジサイドで言語判定を実行することで、オリジンサーバーへのリクエスト前に処理が完了します。結果として、ネットワークレイテンシが削減され、ユーザーにより高速な体験を提供できます。また、不要なオリジンリクエストが減少するため、サーバー負荷も軽減されます。

Q: Accept-Language ヘッダーだけで十分では?

A: Accept-Languageヘッダーは、ユーザーのブラウザ設定に基づいています。しかし、ユーザーが一度言語を手動選択した場合、その選択を忘れずに保持する必要があります。Cookieを併用することで、ユーザーの明示的な選択を優先しながら、初回訪問時にはAccept-Languageを参照できます。

Q: SEO的にリダイレクトは大丈夫?

A: 301(恒久的)リダイレクトではなく、302(一時的)リダイレクトを使用し、かつボット検出によりGooglebot等のクローラーにはリダイレクトしません。これにより、検索エンジンは各言語版を個別にインデックスでき、SEOへの悪影響を回避できます。

要件

  • 初回訪問時にAccept-Languageヘッダーから言語を自動判定
  • Cookie pref-lang による言語選択の永続化(1年間有効)
  • Cloudflare cf.country による地理位置情報フォールバック
  • SEO維持のためクローラー(Googlebot、Bingbot等)にはリダイレクトしない
  • クエリパラメータの保持(?tag=wordpress&page=2 など)
  • VitePressの既存言語切り替え機能との互換性維持

実装方針

  1. Cloudflare Pages Functions (functions/_middleware.ts) で言語判定ロジックを実装
  2. 5段階の優先度で言語を判定:
    • Cookie pref-lang → Accept-Language → cf.country → デフォルト(en)
  3. 定数管理 (functions/constants.ts) で設定を一元化
  4. クライアント側永続化 (language-persistence.ts) でVitePressテーマと統合
  5. 包括的テスト (35テストケース) で品質担保

言語判定フロー

以下のフローチャートで、5段階の優先度に基づいた言語判定プロセスを図示しています:

flowchart TD
    A[リクエスト受信] --> B{Bot/Crawler<br/>か?}
    B -->|はい| Z[リダイレクトなし]
    B -->|いいえ| C{Cookie<br/>pref-lang<br/>存在?}

    C -->|はい| D["lang = Cookie値<br/>を使用"]
    C -->|いいえ| E{Accept-Language<br/>ヘッダー<br/>解析可能?}

    E -->|はい| F["lang = Accept-Language<br/>から言語を抽出"]
    E -->|いいえ| G{cf.country<br/>マップに<br/>存在?}

    G -->|はい| H["lang = 地理位置情報<br/>から言語を判定"]
    G -->|いいえ| I["lang = デフォルト<br/>en を使用"]

    D --> J{現在の言語<br/>と異なる?}
    F --> J
    H --> J
    I --> J

    J -->|はい| K["リダイレクト実行<br/>Set-Cookie: pref-lang"]
    J -->|いいえ| L[リダイレクトなし]
    K --> M[新しい言語ページへ]
    L --> M
    Z --> M

ポイント

Immutable Headers問題の解決

Cloudflare WorkersではResponse.redirect()context.next()から返されるResponseオブジェクトのヘッダーは不変(immutable)。

問題のあるコード:

const response = Response.redirect(redirectUrl, 302);
response.headers.append('Set-Cookie', ...); // エラー: 不変ヘッダーを変更できない

解決策: 新しいHeadersオブジェクトを作成してからResponseを構築

function createResponseWithCookie(response: Response, lang: Language): Response {
  const headers = new Headers(response.headers);
  headers.append('Set-Cookie', generateCookieString(lang));
  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers
  });
}

ボット検出パターン

10以上の主要クローラーを正規表現で検出し、SEOを維持:

export const BOT_PATTERNS = {
  USER_AGENTS: /bot|crawler|spider|crawling|googlebot|bingbot|slurp|duckduckbot|baiduspider|yandexbot|facebookexternalhit|twitterbot|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator/i,
} as const;

Cookie セキュリティ設定

export const COOKIE_OPTIONS = {
  path: '/',
  httpOnly: true,  // XSS攻撃防止:JavaScriptからのアクセスをブロック
  secure: true,    // HTTPS接続時のみ送信(中間者攻撃防止)
  sameSite: 'lax', // CSRF攻撃防止:クロスサイトリクエストでのCookie送信を制限
} as const;

セキュリティ属性の詳細:

  • httpOnly: Cookie がクライアント側の JavaScript(XSS攻撃)からアクセスされることを防止します
  • secure: HTTPS接続時のみ Cookie を送信し、平文のHTTP通信での盗聴を防ぎます
  • sameSite: 'lax': クロスサイトリクエストでの Cookie 送信を制限し、CSRF攻撃を防止します

これらの設定により、ユーザーの言語選択情報は高いセキュリティレベルで保護されます。

学び

  1. Cloudflare Workers APIの特性: Immutable Responseの扱いは標準Node.jsと異なる
  2. エッジサイド処理の利点: オリジンへのリクエスト前に言語判定が完了し、レイテンシが大幅に削減される
  3. サーバー/クライアント連携: サーバー側httpOnly Cookieとクライアント側オブザーバーの組み合わせで、セキュリティとUXを両立
  4. 包括的テストの重要性: 35件のテストケースにより、エッジケースを含む全検出パスをカバー
  5. SEOとユーザビリティの両立: ボット検出により、検索エンジンには全言語版を適切にインデックスさせつつ、人間ユーザーには最適な言語を自動提供

まとめ

本ガイドでは、Cloudflare Pages Functions を活用したEdge-side i18n(国際化)リダイレクト の実装方法を解説しました。

実装のポイント

  1. Immutable Response の理解: Cloudflare Workers の Response オブジェクトは不変のため、新しい Headers を作成してから Response を構築する必要があります

  2. 5段階の優先度フロー: Cookie → Accept-Language → cf.country → デフォルト という段階的な言語判定により、ユーザーの意図を最優先しながら初回訪問時には Accept-Language から言語を自動判定できます

  3. SEO 対策とボット検出: 302リダイレクト(一時的)と BOT検出の組み合わせにより、検索エンジンクローラーには各言語版を個別にインデックスさせながら、一般ユーザーには最適な言語版を自動提供できます

  4. セキュアな Cookie 実装: httpOnlysecuresameSite 属性により、XSS攻撃、中間者攻撃、CSRF攻撃を防止しながら、ユーザーの言語選択を安全に保持できます

適用可能なシーン

このパターンは、以下のような多言語 Web サイトに適用可能です:

  • 国際的な SaaS プロダクト: ユーザーの地理位置情報や言語設定に基づいた自動リダイレクト
  • オープンソースプロジェクト: ドキュメントサイトの多言語対応
  • コンテンツメディア: 複数言語で提供するニュースサイトやブログ

35件のテストケースによる包括的な検証により、エッジケースを含む全検出パスをカバーしているため、実装の信頼性も高く、本番環境への適用も安心です。

さらに学ぶ

このパターンに関連する技術について、さらに詳しく学びたい場合は以下のトピックを推奨します:

  • Cloudflare Workers: エッジサイド処理の基礎と応用
  • HTTPヘッダー仕様: Accept-Language、User-Agent の正規表現マッチング
  • セキュアなCookie実装: httpOnly、secure、sameSite 属性の組み合わせ
  • 多言語SEO: hreflang タグと言語別ページのインデックス戦略
  • エッジコンピューティング: CDNレベルでのロジック実装とパフォーマンス最適化