暗黙的レンダリング(implicit rendering)または明示的レンダリング(explicit rendering)で、ウェブページに Turnstile ウィジェットを追加する方法を説明します。
Turnstile には、ページへウィジェットを追加する方法が 2 つあります。暗黙的レンダリング は、ページ読み込み時に HTML 内のウィジェットコンテナを自動でスキャンします。明示的レンダリング は、JavaScript で任意のタイミングにウィジェットを作成できます。フォームがページ読み込み時から存在する静的ページには、暗黙的レンダリングを使います。初回読み込みのあとにフォームを作る動的コンテンツやシングルページアプリケーション(SPA)には、明示的レンダリングを使います。
| 機能 | 暗黙的レンダリング | 明示的レンダリング |
|---|---|---|
| セットアップの簡単さ | シンプルでコードは最小限 | 追加の JavaScript が必要 |
| タイミングの制御 | ページ読み込み時に自動でレンダリング | レンダリングタイミングを完全に制御 |
| ユースケース | 静的コンテンツ | 動的またはインタラクティブなコンテンツ |
| カスタマイズ | HTML 属性に限定 | JavaScript API で幅広く設定可能 |
開始する前に、次が必要です。
- Cloudflare アカウント
- sitekey 付きの Turnstile ウィジェット
- ウェブサイトの HTML を編集できること
- HTML と JavaScript の基礎知識
- ページ読み込み: Turnstile のスクリプトが読み込まれ、要素をスキャンするか、プログラムからの呼び出しを待ちます。
- ウィジェットのレンダリング: ウィジェットが作成され、チャレンジの実行を開始します。
- トークン生成: チャレンジが完了すると、トークンが生成されます。
- フォーム連携: トークンはコールバックまたは隠しフォームフィールドで利用できます。
- サーバー検証: サーバーがトークンを受け取り、Siteverify API で検証します。
暗黙的レンダリングは、cf-turnstile クラスを持つ要素を HTML から自動でスキャンし、追加の JavaScript なしでウィジェットをレンダリングします。ページ読み込みと同時にウィジェットを表示したい静的ページに適しています。
Cloudflare では、次の場合に暗黙的レンダリングを推奨します。
- シンプルな実装で、すばやく組み込みたい。
- フォーム構成が単純な静的サイトである。
- ページ読み込み時にウィジェットをすぐ表示したい。
- ウィジェットをプログラムから制御する必要がない。
Turnstile のスクリプトを含める: Turnstile の JavaScript API を、HTML の <head> 内、または閉じタグ </body> の直前に追加します。
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
async
defer
></script>リソースヒントを追加すると、Cloudflare サーバーへの接続を早めに確立し、読み込み性能を改善できます。この <link> タグは、Turnstile のスクリプトより前に、HTML の <head> へ置きます。
<link rel="preconnect" href="https://challenges.cloudflare.com" />チャレンジを表示したい場所に、ウィジェットのコンテナを追加します。
<div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>data 属性で ウィジェットをカスタマイズ します。ウィジェットを表示したい場所に div 要素を挿入します。
<div
class="cf-turnstile"
data-sitekey="<YOUR-SITE-KEY>"
data-theme="light"
data-size="normal"
data-callback="onSuccess"
></div>チャレンジが解決されると、成功コールバックへトークンが渡されます。このトークンは Siteverify エンドポイント で検証する必要があります。
基本的なログインフォーム
Turnstile は、ログインフォームやお問い合わせフォームなど、ウェブサイト上のフォーム保護によく使います。ウィジェットは <form> タグ内に埋め込めます。
<!DOCTYPE html>
<html>
<head>
<title>Login Form</title>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head>
<body>
<form action="/login" method="POST">
<input type="text" name="username" placeholder="Username" autocomplete="username" required />
<input type="password" name="password" placeholder="Password" autocomplete="current-password" required />
<!-- Turnstile widget with basic configuration -->
<div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>
<button type="submit">Log in</button>
</form>
</body>
</html>名前が cf-turnstile-response の不可視の入力が追加され、他のフィールドと一緒にサーバーへ送られます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Implicit Rendering with Cloudflare Turnstile</title>
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
async
defer
></script>
</head>
<body>
<h1>Contact Us</h1>
<form action="/submit" method="POST">
<label for="name">Name:</label><br />
<input type="text" id="name" name="name" required /><br />
<label for="email">Email:</label><br />
<input type="email" id="email" name="email" required /><br />
<!-- Turnstile Widget -->
<div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>
<br />
<button type="submit">Submit</button>
</form>
</body>
</html>コールバック付きの高度なフォーム
<form action="/contact" method="POST" id="contact-form">
<input type="email" name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required></textarea>
<!-- Widget with callbacks and custom configuration -->
<div
class="cf-turnstile"
data-sitekey="<YOUR-SITE-KEY>"
data-theme="auto"
data-size="flexible"
data-callback="onTurnstileSuccess"
data-error-callback="onTurnstileError"
data-expired-callback="onTurnstileExpired"
></div>
<button type="submit" id="submit-btn" disabled>Send Message</button>
</form>
<script>
function onTurnstileSuccess(token) {
console.log("Turnstile success:", token);
document.getElementById("submit-btn").disabled = false;
}
function onTurnstileError(errorCode) {
console.error("Turnstile error:", errorCode);
document.getElementById("submit-btn").disabled = true;
}
function onTurnstileExpired() {
console.warn("Turnstile token expired");
document.getElementById("submit-btn").disabled = true;
}
</script>設定が異なる複数のウィジェット
<!-- Compact widget for newsletter signup -->
<form action="/newsletter" method="POST">
<input type="email" name="email" placeholder="Email" />
<div
class="cf-turnstile"
data-sitekey="<YOUR-SITE-KEY>"
data-size="compact"
data-action="newsletter"
></div>
<button type="submit">Subscribe</button>
</form>
<!-- Normal widget for contact form -->
<form action="/contact" method="POST">
<input type="text" name="name" placeholder="Name" />
<input type="email" name="email" placeholder="Email" />
<textarea name="message" placeholder="Message"></textarea>
<div
class="cf-turnstile"
data-sitekey="<YOUR-SITE-KEY>"
data-action="contact"
data-theme="dark"
></div>
<button type="submit">Send</button>
</form>フォームへの自動連携
Turnstile ウィジェットを <form> 要素内に埋め込むと、名前が cf-turnstile-response の不可視の入力フィールドが自動作成されます。このフィールドには検証トークンが入り、他のフォームデータと一緒に送信されます。
<form action="/submit" method="POST">
<input type="text" name="data" />
<div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>
<!-- Hidden field automatically added: -->
<!-- <input type="hidden" name="cf-turnstile-response" value="TOKEN_VALUE" /> -->
<button type="submit">Submit</button>
</form>明示的レンダリングでは、ウィジェットの表示タイミングと場所、作成方法を JavaScript 関数でプログラム制御できます。動的コンテンツ、シングルページアプリケーション(SPA)、ユーザー操作に応じた条件付きレンダリングに適しています。
Cloudflare では、次の場合に明示的レンダリングを推奨します。
- 動的サイトやシングルページアプリケーション(SPA)である。
- ウィジェット作成のタイミングを制御したい。
- 訪問者の操作に応じて、条件付きでウィジェットをレンダリングしたい。
- 設定が異なる複数のウィジェットを使いたい。
- ウィジェットのライフサイクル管理が必要な複雑なアプリケーションである。
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"
defer
></script>cf-turnstile クラスなしでコンテナを作成します。
<div id="turnstile-container"></div>ウィジェットを作成する準備ができたら、turnstile.render() を呼び出します。
const widgetId = turnstile.render("#turnstile-container", {
sitekey: "<YOUR-SITE-KEY>",
callback: function (token) {
console.log("Success:", token);
},
});Turnstile ウィジェットを明示的にレンダリングしたあと、アプリケーションの要件に応じて操作が必要になることがあります。ウィジェットの状態管理は、次の各節を参照してください。
タイムアウトまたは期限切れのウィジェットをリセットするには、次の関数を使います。
turnstile.reset(widgetId);現在のレスポンストークンは、いつでも取得できます。
const responseToken = turnstile.getResponse(widgetId);ウィジェットが不要になったら、次の方法でページから削除できます。
turnstile.remove(widgetId);コールバックは呼ばれず、関連する DOM 要素はすべて削除されます。
明示的レンダリングの基本実装
<!DOCTYPE html>
<html>
<head>
<title>Explicit Rendering</title>
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"
defer
></script>
</head>
<body>
<form id="login-form">
<input
type="text"
name="username"
placeholder="Username"
autocomplete="username"
/>
<input
type="password"
name="password"
placeholder="Password"
autocomplete="current-password"
/>
<div id="turnstile-widget"></div>
<button type="submit">Login</button>
</form>
<script>
window.onload = function () {
turnstile.render("#turnstile-widget", {
sitekey: "<YOUR-SITE-KEY>",
callback: function (token) {
console.log("Turnstile token:", token);
// Handle successful verification
},
"error-callback": function (errorCode) {
console.error("Turnstile error:", errorCode);
},
});
};
</script>
</body>
</html>onload コールバックを使う
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=onTurnstileLoad"
defer
></script>
<div id="widget-container"></div>
<script>
function onTurnstileLoad() {
turnstile.render("#widget-container", {
sitekey: "<YOUR-SITE-KEY>",
theme: "light",
callback: function (token) {
console.log("Challenge completed:", token);
},
});
}
</script>高度な SPA 実装
<div id="dynamic-form-container"></div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"></script>
<script>
class TurnstileManager {
constructor() {
this.widgets = new Map();
}
createWidget(containerId, config) {
// Wait for Turnstile to be ready
turnstile.ready(() => {
const widgetId = turnstile.render(containerId, {
sitekey: config.sitekey,
theme: config.theme || "auto",
size: config.size || "normal",
callback: (token) => {
console.log(`Widget ${widgetId} completed:`, token);
if (config.onSuccess) config.onSuccess(token, widgetId);
},
"error-callback": (error) => {
console.error(`Widget ${widgetId} error:`, error);
if (config.onError) config.onError(error, widgetId);
},
});
this.widgets.set(containerId, widgetId);
return widgetId;
});
}
removeWidget(containerId) {
const widgetId = this.widgets.get(containerId);
if (widgetId) {
turnstile.remove(widgetId);
this.widgets.delete(containerId);
}
}
resetWidget(containerId) {
const widgetId = this.widgets.get(containerId);
if (widgetId) {
turnstile.reset(widgetId);
}
}
}
// Usage
const manager = new TurnstileManager();
// Create a widget when user clicks a button
document.getElementById("show-form-btn").addEventListener("click", () => {
document.getElementById("dynamic-form-container").innerHTML = `
<form>
<input type="email" placeholder="Email" />
<div id="turnstile-widget"></div>
<button type="submit">Submit</button>
</form>
`;
manager.createWidget("#turnstile-widget", {
sitekey: "<YOUR-SITE-KEY>",
theme: "dark",
onSuccess: (token) => {
// Handle successful verification
console.log("Form ready for submission");
},
});
});
</script>明示的レンダリングでは、ウィジェットのライフサイクルを完全に制御できます。
// Render a widget
const widgetId = turnstile.render("#container", {
sitekey: "<YOUR-SITE-KEY>",
callback: handleSuccess,
});
// Get the current token
const token = turnstile.getResponse(widgetId);
// Check if widget is expired
const isExpired = turnstile.isExpired(widgetId);
// Reset the widget (clears current state)
turnstile.reset(widgetId);
// Remove the widget completely
turnstile.remove(widgetId);実行モードで、チャレンジを動かすタイミングを制御します。
// Render widget but don't run challenge yet
const widgetId = turnstile.render("#container", {
sitekey: "<YOUR-SITE-KEY>",
execution: "execute", // Don't auto-execute
});
// Later, run the challenge when needed
turnstile.execute("#container");訪問者がページに入ったできるだけ早い時点で Turnstile のスクリプトを実行することを推奨します。検証が完了し、訪問者がページ上で操作を試みる時点で、インタラクションを使える状態にします。
暗黙的レンダリングと明示的レンダリングは、同じ設定オプションに対応します。よく使う設定は次の表を参照してください。
| オプション | 説明 | 値 |
|---|---|---|
sitekey |
ウィジェットの sitekey | 必須の文字列 |
theme |
見た目のテーマ | auto、light、dark |
size |
ウィジェットサイズ | normal、flexible、compact |
callback |
成功時のコールバック | 関数 |
error-callback |
エラー時のコールバック | 関数 |
execution |
チャレンジを実行するタイミング | render、execute |
appearance |
ウィジェットを表示するタイミング | always、execute、interaction-only |
設定オプションの全一覧は、ウィジェットの設定 を参照してください。
テスト用のサイトキーを使うと、実際の Cloudflare Challenge を発生させずに、ウェブページ上の Turnstile ウィジェットをテストできます。
詳細は テスト を参照してください。
Turnstile は、http:// または https:// の URI スキームを使うページでのみ動作します。file:// など、その他のプロトコルではウィジェットの埋め込みに対応していません。
-
サーバー側での検証は必須です。Turnstile トークンは Siteverify API で必ず検証してください。Turnstile トークンは無効、期限切れ、またはすでに使用済みの場合があります。トークンを検証しないと、実装に重大な脆弱性が残ります。Turnstile の設定を完了するには、Siteverify を呼び出す必要があります。呼び出さないと設定は不完全なままとなり、Turnstile Analytics でメトリクスを確認したときにトークン検証がゼロになります。
-
トークンは 300 秒(5 分)で期限切れになります。各トークンは 1 回だけ検証できます。期限切れまたは使用済みのトークンは、新しいチャレンジに置き換える必要があります。