Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

ウィジェットを埋め込む

最終更新 Markdown で表示Agent セットアップ

暗黙的レンダリング(implicit rendering)または明示的レンダリング(explicit rendering)で、ウェブページに Turnstile ウィジェットを追加する方法を説明します。

Turnstile には、ページへウィジェットを追加する方法が 2 つあります。暗黙的レンダリング は、ページ読み込み時に HTML 内のウィジェットコンテナを自動でスキャンします。明示的レンダリング は、JavaScript で任意のタイミングにウィジェットを作成できます。フォームがページ読み込み時から存在する静的ページには、暗黙的レンダリングを使います。初回読み込みのあとにフォームを作る動的コンテンツやシングルページアプリケーション(SPA)には、明示的レンダリングを使います。

機能 暗黙的レンダリング 明示的レンダリング
セットアップの簡単さ シンプルでコードは最小限 追加の JavaScript が必要
タイミングの制御 ページ読み込み時に自動でレンダリング レンダリングタイミングを完全に制御
ユースケース 静的コンテンツ 動的またはインタラクティブなコンテンツ
カスタマイズ HTML 属性に限定 JavaScript API で幅広く設定可能

前提条件

開始する前に、次が必要です。

  • Cloudflare アカウント
  • sitekey 付きの Turnstile ウィジェット
  • ウェブサイトの HTML を編集できること
  • HTML と JavaScript の基礎知識

処理の流れ

  1. ページ読み込み: Turnstile のスクリプトが読み込まれ、要素をスキャンするか、プログラムからの呼び出しを待ちます。
  2. ウィジェットのレンダリング: ウィジェットが作成され、チャレンジの実行を開始します。
  3. トークン生成: チャレンジが完了すると、トークンが生成されます。
  4. フォーム連携: トークンはコールバックまたは隠しフォームフィールドで利用できます。
  5. サーバー検証: サーバーがトークンを受け取り、Siteverify API で検証します。

暗黙的レンダリング

暗黙的レンダリングは、cf-turnstile クラスを持つ要素を HTML から自動でスキャンし、追加の JavaScript なしでウィジェットをレンダリングします。ページ読み込みと同時にウィジェットを表示したい静的ページに適しています。

ユースケース

Cloudflare では、次の場合に暗黙的レンダリングを推奨します。

  • シンプルな実装で、すばやく組み込みたい。
  • フォーム構成が単純な静的サイトである。
  • ページ読み込み時にウィジェットをすぐ表示したい。
  • ウィジェットをプログラムから制御する必要がない。

実装

1. Turnstile のスクリプトを追加する

Turnstile のスクリプトを含める: Turnstile の JavaScript API を、HTML の <head> 内、または閉じタグ </body> の直前に追加します。

<script
	src="https://challenges.cloudflare.com/turnstile/v0/api.js"
	async
	defer
></script>

2.(任意)リソースヒントでパフォーマンスを最適化する

リソースヒントを追加すると、Cloudflare サーバーへの接続を早めに確立し、読み込み性能を改善できます。この <link> タグは、Turnstile のスクリプトより前に、HTML の <head> へ置きます。

<link rel="preconnect" href="https://challenges.cloudflare.com" />

3. ウィジェット要素を追加する

チャレンジを表示したい場所に、ウィジェットのコンテナを追加します。

<div class="cf-turnstile" data-sitekey="<YOUR-SITE-KEY>"></div>

4. data 属性で設定する

data 属性で ウィジェットをカスタマイズ します。ウィジェットを表示したい場所に div 要素を挿入します。

<div
	class="cf-turnstile"
	data-sitekey="<YOUR-SITE-KEY>"
	data-theme="light"
	data-size="normal"
	data-callback="onSuccess"
></div>

チャレンジが解決されると、成功コールバックへトークンが渡されます。このトークンは Siteverify エンドポイント で検証する必要があります。

ユースケース別の暗黙的レンダリングの完成例

基本的なログインフォーム

Turnstile は、ログインフォームやお問い合わせフォームなど、ウェブサイト上のフォーム保護によく使います。ウィジェットは <form> タグ内に埋め込めます。

Examplehtml
<!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 の不可視の入力が追加され、他のフィールドと一緒にサーバーへ送られます。

Complete HTML examplehtml
<!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>

コールバック付きの高度なフォーム

Examplehtml
<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>

設定が異なる複数のウィジェット

Examplehtml
<!-- 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)である。
  • ウィジェット作成のタイミングを制御したい。
  • 訪問者の操作に応じて、条件付きでウィジェットをレンダリングしたい。
  • 設定が異なる複数のウィジェットを使いたい。
  • ウィジェットのライフサイクル管理が必要な複雑なアプリケーションである。

実装

1. 明示的レンダリング用のスクリプトをサイトへ追加する

<script
	src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"
	defer
></script>

2. コンテナ要素を作成する

cf-turnstile クラスなしでコンテナを作成します。

<div id="turnstile-container"></div>

3. ウィジェットをプログラムからレンダリングする

ウィジェットを作成する準備ができたら、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 要素はすべて削除されます。

ユースケース別の明示的レンダリングの完成例

明示的レンダリングの基本実装

Examplehtml
<!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 コールバックを使う

Examplehtml
<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 実装

Examplehtml
<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 見た目のテーマ autolightdark
size ウィジェットサイズ normalflexiblecompact
callback 成功時のコールバック 関数
error-callback エラー時のコールバック 関数
execution チャレンジを実行するタイミング renderexecute
appearance ウィジェットを表示するタイミング alwaysexecuteinteraction-only

設定オプションの全一覧は、ウィジェットの設定 を参照してください。


テスト

テスト用のサイトキーを使うと、実際の Cloudflare Challenge を発生させずに、ウェブページ上の Turnstile ウィジェットをテストできます。

詳細は テスト を参照してください。


制限事項

Turnstile は、http:// または https:// の URI スキームを使うページでのみ動作します。file:// など、その他のプロトコルではウィジェットの埋め込みに対応していません。


セキュリティ要件

  • サーバー側での検証は必須です。Turnstile トークンは Siteverify API で必ず検証してください。Turnstile トークンは無効、期限切れ、またはすでに使用済みの場合があります。トークンを検証しないと、実装に重大な脆弱性が残ります。Turnstile の設定を完了するには、Siteverify を呼び出す必要があります。呼び出さないと設定は不完全なままとなり、Turnstile Analytics でメトリクスを確認したときにトークン検証がゼロになります。

  • トークンは 300 秒(5 分)で期限切れになります。各トークンは 1 回だけ検証できます。期限切れまたは使用済みのトークンは、新しいチャレンジに置き換える必要があります。

役に立ちましたか?