Turnstile を設定する
Turnstile のウィジェットを作り、2 つのキーを設定して、問い合わせフォームを使えるようにします。
このページの手順で Turnstile のウィジェットを作り、サイトキーとシークレットキーを設定します。両方を設定すると、問い合わせフォームが送信を受け付けるようになります。
Turnstile は、自動プログラムによるフォームの送信を防ぐための、Cloudflare の無料の機能です。goodnight-web の問い合わせフォームには Turnstile が必要です。
ウィジェットを作る
- Cloudflare のダッシュボードで Turnstile を開き、Add widget を選びます。
- Widget name: 見分けやすい名前を付けます。たとえば会社名です。
- Hostnames: サイトのドメインを入れます。たとえば
example.comです。試している間にworkers.devのアドレスも使うなら、それも追加します。workers.devのアドレスを無効にするときに外してください。 - Widget mode: 迷ったら Managed にします。ほとんどの訪問者には短い確認が出るだけで、一部の人にはクリックを求めます。
- Create を選びます。
Cloudflare が 2 つの値を表示します(Cloudflare: ダッシュボードでウィジェットを作る)。
- サイトキー(Site key): 公開してよい値です。問い合わせページがウィジェットを表示するのに使います。
- シークレットキー(Secret key): 秘密の値です。Worker が Turnstile の検証 API(siteverify)で送信を確かめるのに使います。
キーを設定する
| キー | 置く場所 | 公開してよいか |
|---|---|---|
| サイトキー | Worker の変数 TURNSTILE_SITE_KEY | はい。問い合わせページが /api/contact/form から読み取ります。 |
| シークレットキー | Worker のシークレット TURNSTILE_SECRET | いいえ。コミットしたり、ページに入れたりしないでください。 |
サイトキー
starter では、サイトキーを wrangler.jsonc に書きます。変更をコミットすると、次のデプロイで反映されます。
"vars": {
"TURNSTILE_SITE_KEY": "0x4AAAAAAA...",
},設定するのはここだけです。問い合わせページにはキーを書かず、Worker から読み込みます。
ダッシュボードでは設定しないでください。ダッシュボードだけで設定した値は、次のデプロイで wrangler.jsonc の値に置き換わります。wrangler deploy がファイルの変数をアップロードするためです。
シークレットキー
Cloudflare のダッシュボードで Worker を開き、変数とシークレットに TURNSTILE_SECRET を種類 Secret で追加します。プロジェクトのフォルダーでターミナルを使うなら、次のコマンドでも同じことができます。
npx wrangler secret put TURNSTILE_SECRET設定する前の動き
goodnight-web は、必要な設定がそろうまで機能を止めます(フェイルクローズ)。Turnstile がないと、問い合わせ API はボットの送信も受け付けてしまいます。そのため、受け付けそのものを止めます。
| 場所 | 設定する前の動き |
|---|---|
POST /api/contact | 503 を返します。 |
| 問い合わせページ | 「お問い合わせフォームは準備中です」と表示します。 |
| 管理画面 | 「お問い合わせフォームは受け付けを停止しています」というお知らせと、このページへのリンクを表示します。 |


訪問者には、送れたように見えて届かないフォームではなく、準備中の表示が見えます。両方のキーを設定すれば、ほかに何も変えずにフォームが動き始めます。
Worker が確かめること
Worker は送信のたびに、フォームから届いたトークンを siteverify に送ります。次をすべて満たすときだけ受け付けます。
successが true である。hostnameがあなたのサイトのホスト名である。リクエストが来たホスト名か、createGoodnightWorker()のcontact.turnstileHostnamesオプションに並べたホスト名のどれかです。actionが、問い合わせフォームで設定した action である。
トークンの有効期限は 300 秒で、検証できるのは 1 回だけです(Cloudflare: サーバー側の検証)。確認に失敗したら 400 を返し、何も保存しません。
ほかの確認は問い合わせフォームの保護で説明しています。
ローカルで開発する
wrangler dev でフォームの表示を確かめるときは、本物のキーではなく Cloudflare のテスト用キーを使います(Cloudflare: テスト)。この組み合わせでは、ウィジェットは必ず成功し、siteverify はどのトークンも受け付けます。ただし、送信は Worker の確認で拒否されます(次の節)。
site key: 1x00000000000000000000AA
secret key: 1x0000000000000000000000000000000AAテスト用キーは、Git が無視する .dev.vars(starter の .dev.vars.example をコピーしたファイル)に書きます。wrangler.jsonc にはコミットしないでください。
テスト用キーは本番のサイトで使わないでください。すべてのリクエストを通してしまいます。
テスト用キーでは送信が拒否される
テスト用のシークレットキーでは、siteverify は hostname を example.com として返し、action を返しません(2026-10-22 に確認)。
{
"success": true,
"hostname": "example.com",
"metadata": { "result_with_testing_key": true }
}Worker は hostname と action も確かめるので、テスト用キーでの送信は拒否します。フォームには、確認に失敗したと表示します。
これは意図したものです。ローカルの開発のために、本番の確認を緩めることはしません。
starter の E2E テスト
starter の E2E テスト(npm run test:e2e)は、次の順に確かめます。
- テスト用キーでの送信が、上のとおり拒否されること。
- テスト専用の Worker
e2e/worker.tsを起動し、フォームの送信から D1 への保存までが通ること。この Worker は、siteverify の応答にresult_with_testing_keyが付いているときに限り、hostnameとactionを期待どおりの値にします。
e2e/worker.ts はデプロイしないでください。