フロントエンド側
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<div class="cf-turnstile" data-sitekey="サイトキー" data-callback="onTurnstileSuccess" data-expired-callback="onTurnstileExpired" data-error-callback="onTurnstileError" data-language="ja"></div>
<script>
let turnstileToken = null;
function onTurnstileSuccess(token) {
console.log("✅ Turnstile 成功:", token);
turnstileToken = token;
document.getElementById("submit-btn").disabled = false;
}
function onTurnstileExpired() {
console.log("⏳ Turnstile 期限切れ");
turnstileToken = null;
document.getElementById("submit-btn").disabled = true;
}
function onTurnstileError() {
console.log("❌ Turnstile エラー");
turnstileToken = null;
document.getElementById("submit-btn").disabled = true;
}
function isTurnstileCompleted() {
return !!turnstileToken;
}
</script>
サーバー側
$data = [];
$data[ 'secret' ] = 'シークレットキー';
$data[ 'response' ] = $_POST[ 'cf-turnstile-response' ];
$data = http_build_query( $data, '', '&' );
$curl = curl_init();
curl_setopt_array( $curl, [
CURLOPT_URL => 'https://challenges.cloudflare.com/turnstile/v0/siteverify',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $data,
] );
$res = curl_exec( $curl );
$err = curl_error( $curl );
curl_close( $curl );
if ( $err || ! isset( $_POST[ 'cf-turnstile-response' ] ) || $_POST[ 'cf-turnstile-response' ] === '' ) {
exit;
}