--- title: "Edge-side i18nリダイレクト実装ガイド | Cloudflare Pages Functionsで多言語対応" description: "Accept-LanguageヘッダーとCookieを使用した自動言語判定、SEO対策(ボット検出)、セキュアなCookie実装を完全解説。Cloudflare Pages Functionsでの実装手順を35個のテストケース付きで紹介。" created: "2026-01-08" status: "published" platforms: wordpress: published: true wp_status: "future" url: "https://wakatchi.dev/?p=2537" published_date: "2026-02-03T06:00:00+09:00" post_id: 2537 slug: "edge-side-i18n-cloudflare" tags: ["i18n", "Edge Functions", "VitePress", "TypeScript", "Cookie", "Accept-Language"] --- ![Cloudflare Pages Functionsによるエッジサイドi18nリダイレクトの仕組み。Cookie、Accept-Language、国情報を優先順位として判定し、ユーザーを適切な言語サイト(英語/日本語)へ誘導しつつ、BotのSEOも維持するフロー図。](https://wakatchi.dev/wp-content/uploads/2026/02/Gemini_Generated_Image_idadcfidadcfidad.webp) ## 背景 多言語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)](#よくある質問faq) - [要件](#要件) - [実装方針](#実装方針) - [ポイント](#ポイント) - [Immutable Headers問題の解決](#immutable-headers問題の解決) - [ボット検出パターン](#ボット検出パターン) - [Cookie セキュリティ設定](#cookie-セキュリティ設定) - [学び](#学び) - [参考情報](#参考情報) ## よくある質問(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段階の優先度に基づいた言語判定プロセスを図示しています: ```mermaid 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)。 **問題のあるコード**: ```typescript const response = Response.redirect(redirectUrl, 302); response.headers.append('Set-Cookie', ...); // エラー: 不変ヘッダーを変更できない ``` **解決策**: 新しいHeadersオブジェクトを作成してからResponseを構築 ```typescript 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を維持: ```typescript 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 セキュリティ設定 ```typescript 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 実装**: `httpOnly`、`secure`、`sameSite` 属性により、XSS攻撃、中間者攻撃、CSRF攻撃を防止しながら、ユーザーの言語選択を安全に保持できます ### 適用可能なシーン このパターンは、以下のような多言語 Web サイトに適用可能です: - **国際的な SaaS プロダクト**: ユーザーの地理位置情報や言語設定に基づいた自動リダイレクト - **オープンソースプロジェクト**: ドキュメントサイトの多言語対応 - **コンテンツメディア**: 複数言語で提供するニュースサイトやブログ 35件のテストケースによる包括的な検証により、エッジケースを含む全検出パスをカバーしているため、実装の信頼性も高く、本番環境への適用も安心です。 ## さらに学ぶ このパターンに関連する技術について、さらに詳しく学びたい場合は以下のトピックを推奨します: - **Cloudflare Workers**: エッジサイド処理の基礎と応用 - **HTTPヘッダー仕様**: Accept-Language、User-Agent の正規表現マッチング - **セキュアなCookie実装**: httpOnly、secure、sameSite 属性の組み合わせ - **多言語SEO**: hreflang タグと言語別ページのインデックス戦略 - **エッジコンピューティング**: CDNレベルでのロジック実装とパフォーマンス最適化

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レベルでのロジック実装とパフォーマンス最適化