> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tryprofound.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cloudflare のセットアップ

> 1 つの Cloudflare Worker で AI アシスタントのトラフィックを Profound Dynamic Bot Rendering にルーティングし、人間と検索クローラーのトラフィックはオリジンのまま維持します。

このガイドでは、ゾーンに Cloudflare Worker を 1 つ追加します。Worker は `User-Agent` を検査し、一致する AI アシスタントのリクエストを Profound に転送し、それ以外はすべてそのままオリジンに渡します。

Worker は Profound の汎用エンドポイント `GET https://concierge.tryprofound.com/v1/concierge` を呼び出し、元のホストとパスをヘッダーとして渡します。

<Warning>
  `/*` ルート上の Worker は、そのホスト名へのすべてのリクエストで実行されるため、本番環境のリクエスト経路上に配置されることになります。このコードは、フェッチの失敗、レスポンスヘッダーの期限超過、およびステータス `400`、`401`、`403`、`500`、`502`、`503`、`504` の場合にオリジンへ再試行します。リダイレクト、`404`、`429` を含むその他のステータスはそのまま返します。公開ページの許可リストとレイテンシーの予算を調整し、まずステージング用のホスト名でテストしてください。レスポンスのストリーミング開始後に発生した障害については、部分的に送信されたボディを透過的に置き換えることはできません。
</Warning>

## 前提条件

* ゾーンで Workers が有効になっている Cloudflare アカウント
* ホスト名に対する既存のプロキシ済み DNS レコードと、それを通じて到達可能なオリジン。Worker Custom Domain ではなく、[Worker Route](https://developers.cloudflare.com/workers/configuration/routing/routes/) を使用してください。
* 既存の Worker ルートおよび重複する Worker ルートの一覧。それらを置き換えるのではなく、ロジックを維持してください。組み合わせるために設計の変更が必要な場合は、作業を中止し、別途レビューしてください。
* ルートの障害モードの記録と、[Workers の制限](https://developers.cloudflare.com/workers/platform/limits/)に対するキャパシティの確認。クォータ枯渇時のフェイルオープンは、Worker をバイパスしても既存のセキュリティが維持される場合にのみ使用してください。フェイルクローズは、JavaScript の catch が実行される前に、人間のトラフィックにも影響する可能性があります。
* ホスト名に Worker ルートを追加する権限
* Dynamic Bot Rendering の API キー、登録済みのドメイン、配信ポリシー、および既知の公開済みテストページ。[始める前に](/ja-jp/dynamic-bot-rendering/overview#before-you-start)を参照してください。

## セットアップ

<Steps>
  <Step title="Worker を作成する">
    Cloudflare ダッシュボードで **Workers & Pages → Create → Create Worker** を開きます。名前を `profound-bot-rendering` とし、Worker が作成されるようにプレースホルダーをデプロイします。
  </Step>

  <Step title="Worker のコードを追加する">
    新しい Worker で **Edit code** を開き、以下を追加します。4 つのサンプルパスは、レビュー済みの公開ページに置き換えてください。既存の Worker ロジックを上書きしないでください。

    ```js theme={null}
    const CONCIERGE_ENDPOINT = 'https://concierge.tryprofound.com/v1/concierge';

    // Keep this list in sync with the supported assistants in the Profound docs.
    const BOT_RE = /duckassistbot|chatgpt-user|gemini-deep-research|perplexity-user|amzn-user|mistralai-user|claude-user|claude-code|codex/i;

    // Explicit public pages only. Review each path before extending this list.
    const PUBLIC_PATHS = new Set(['/', '/pricing', '/docs', '/docs/getting-started']);
    const UPSTREAM_TIMEOUT_MS = 2000; // Example header deadline; leave time for origin fallback.
    const FORWARDED_HEADERS = ['user-agent', 'accept', 'accept-encoding', 'accept-language'];

    // Statuses that mean "Profound could not serve this" rather than "the origin
    // said so". 404 is deliberately absent: Profound fails open internally and
    // returns your origin's own status, so a 404 is your real 404.
    const FAILOVER_STATUSES = new Set([400, 401, 403, 500, 502, 503, 504]);

    async function failOpen(request) {
      // A subrequest to the same zone goes to the origin without re-running this Worker.
      const originResponse = await fetch(request);
      const response = new Response(originResponse.body, originResponse);
      response.headers.set('x-concierge-cdn-failover', '1');
      return response;
    }

    export default {
      async fetch(request, env) {
        const url = new URL(request.url);
        const userAgent = request.headers.get('user-agent') || '';

        // Loop protection: Profound stamps this on its own origin fetches.
        // Passing it through means those fetches reach the origin directly.
        if (request.headers.has('x-concierge-request')) return fetch(request);

        if (request.method !== 'GET') return fetch(request);
        if (request.headers.has('cookie') || request.headers.has('authorization')) return fetch(request);

        const isAssistant = BOT_RE.test(userAgent);
        if (!isAssistant || !PUBLIC_PATHS.has(url.pathname)) return fetch(request);

        // Copy only required negotiation headers, never viewer credentials or identity.
        const headers = new Headers();
        for (const name of FORWARDED_HEADERS) {
          const value = request.headers.get(name);
          if (value !== null) headers.set(name, value);
        }

        headers.set('x-concierge-api-key', env.PROFOUND_CONCIERGE_KEY);
        headers.set('x-concierge-host', url.hostname);
        headers.set('x-concierge-url', url.pathname + url.search);

        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), UPSTREAM_TIMEOUT_MS);
        try {
          const response = await fetch(CONCIERGE_ENDPOINT, {
            method: 'GET',
            headers,
            redirect: 'manual', // pass redirects through instead of following them
            cache: 'no-store', // also verify the endpoint owner's cache policy
            signal: controller.signal,
          });

          if (FAILOVER_STATUSES.has(response.status)) {
            if (response.body) void response.body.cancel().catch(() => {});
            controller.abort();
            return failOpen(request);
          }
          const result = new Response(response.body, response);
          result.headers.set('cache-control', 'no-store');
          return result;
        } catch {
          return failOpen(request);
        } finally {
          clearTimeout(timeout);
        }
      },
    };
    ```

    このサンプルでは、Profound のレスポンスヘッダーを待つ時間を 2 秒としています。アシスタントのレイテンシー予算の範囲内で、オリジンのレスポンスに十分な時間を残せるような上限値を選択してください。この期限はヘッダーの到着時点で終了し、レスポンスボディのストリーミングには適用されません。Workers は [AbortController](https://developers.cloudflare.com/workers/runtime-apis/web-standards/) をサポートしています。
  </Step>

  <Step title="API キーをシークレットとして保存する">
    **Settings → Variables and Secrets** を開き、`PROFOUND_CONCIERGE_KEY` という名前のシークレットを追加して、API キーを貼り付けます。保存してデプロイします。

    平文の環境変数ではなく、シークレットを使用してください。シークレットは保存後は書き込み専用となるため、キーはダッシュボードに表示されません。

    Wrangler で同じことを行うには、次のようにします。

    ```bash theme={null}
    npx wrangler secret put PROFOUND_CONCIERGE_KEY
    ```
  </Step>

  <Step title="Worker をホスト名に関連付ける">
    **Settings → Domains & Routes → Add route** を開き、ページトラフィックのパターンを入力して、ゾーンを選択します。

    ```text theme={null}
    www.example.com/*
    ```

    サイトが同じホスト名からアセットを配信している場合でも、Worker はそれらに対して実行され、通常のオリジンフェッチでアセットを返します。その経路から Worker を完全に外すには、コード内のパスチェックに頼るのではなく、ルートパターンを絞り込んでください。
  </Step>

  <Step title="検証する">
    ルーティングを確認したうえで、既知の公開済みページと除外対象のトラフィックを個別に検証します。

    ```bash theme={null}
    curl -sD - -o /dev/null https://www.example.com/pricing -A 'chatgpt-user'
    ```

    HTTP ステータス、有効な `x-concierge-request-id`、`x-concierge-bot-kind: chatgpt-user` を確認します。公開済みのテストページでは、`x-concierge-cache: HIT` と想定どおりの HTML ボディも必須です。通常のブラウザーからのリクエストは、オリジンのルートのまま処理されるはずです。`GET` 以外のリクエスト、API、アセット、認証情報を含むリクエストもテストしてください。

    すべてのチェック項目とヘッダーの意味については、[検証とトラブルシューティング](/ja-jp/dynamic-bot-rendering/verify-and-troubleshoot)を参照してください。
  </Step>
</Steps>

## キャッシュ

Worker のアップストリーム呼び出しは、決してキャッシュしてはいけません。すべてのページが同じアップストリーム URL `https://concierge.tryprofound.com/v1/concierge` を使用し、ページを識別するホスト、パス、クエリは信頼済みヘッダーでのみ送られます。これらのヘッダーをキャッシュキーに含めずにこの URL をキャッシュすると、異なるページやホストが衝突してしまいます。キーに `User-Agent` を追加しても解決しません。

このサンプルでは [`cache: 'no-store'` フェッチ設定](https://developers.cloudflare.com/workers/runtime-apis/fetch/)を使用し、返された Profound のレスポンスに `Cache-Control: no-store` を付与しています。[Cloudflare はキャッシュを閲覧者の URL ではなく、フェッチのサブリクエスト URL に基づいて評価する](https://developers.cloudflare.com/workers/reference/how-the-cache-works/)ため、ご自身のゾーンのキャッシュルールはアップストリーム呼び出しには適用されません。

ロールアウト前に、2 つの公開済みパスとそれぞれのクエリバリアントにアシスタントのリクエストを繰り返し送信し、すべてのレスポンスに新しい `x-concierge-request-id` が含まれていることを確認してください。また、サイトの前段にある他のキャッシュレイヤーについても、アシスタントと人間が分離されているか、置き換えの公開、公開の取り消し、一時停止を行った後にキャッシュエントリーがどうなるかを確認してください。回避策として `User-Agent` だけのキャッシュキーを追加しないでください。エッジで Profound のレスポンスをキャッシュする必要がある場合は、事前に Profound サポートにお問い合わせください。キャッシュキーにはホスト、パス、クエリの識別情報を含め、公開内容の変更時に無効化する必要があります。

## Worker によるサイトの保護

* **アップストリームヘッダーは許可リスト方式です。** ルーティングされるリクエストについて、Worker は `User-Agent`、`Accept`、`Accept-Encoding`、`Accept-Language` のみをコピーし、信頼済みのキー、ホスト、パスを設定します。クライアントが指定した `x-concierge-*` の識別情報や認証情報はコピーしません。Cookie や認可ヘッダーを含むリクエストは、Profound を完全にバイパスします。通常のバイパスリクエストは、ヘッダーを保持したままオリジンに送られます。
* **ループ防止は認証ではありません。** このマーカーは偽装可能なため、WAF ルールやアクセス制御のバイパスには決して使用しないでください。[公開コンテンツの前提条件](/ja-jp/dynamic-bot-rendering/overview#before-you-start)を参照してください。
* **ループ防止は双方向です。** Profound は自身のオリジンフェッチに `x-concierge-request` を付与し、Worker はそれらをそのまま通過させます。また、Profound はこのヘッダーを含む受信リクエストをすべて拒否するため、ルールの設定を誤った場合でも、ループせずに単一のリクエストが失敗するだけで済みます。
* **キーがブラウザーに届くことはありません。** キーは Worker のシークレットに保存され、アップストリーム呼び出し時にのみ付与されます。
* **リダイレクトはそのまま返されます。** `redirect: 'manual'` により、リダイレクトをたどらずにアシスタントへ返すため、リダイレクトの処理はオリジンの判断に委ねられます。
