Php usage -- widget renders OK, $_POST is empty, and site verify fails?

On inspect, I see

<div id="cf-turnstile-c-432161172" class="cf-turnstile" data-sitekey="0x4...T" data-callback="turnstileCommentCallback" data-appearance="always" data-theme="light" data-retry="auto" data-retry-interval="1000" data-size="normal" data-response-field="true" data-response-field-name="cf-turnstile-response">
<iframe style="border: medium none; overflow: hidden; width: 300px; height: 65px;" src="" allow="cross-origin-isolated" id="cf-chl-widget-qm8ou" tabindex="0" title="Widget containing a Cloudflare security challenge"></iframe><input type="hidden" name="cf-turnstile-response" id="cf-chl-widget-qm8ou_response" value="0.y_Fac ... ... ... 724"></div>

Looks like it’s that

value="0.y_Fac ... ... ... 724

that I want to extract somehow and pass to as the token to the CF server verification ?

Yes, that’s it! Now, I can’t comment on why that’s not appearing in the $_POST array but that’s the data you’re after. Do the other form fields appear in the $_POST array or is $_POST completely empty, like you mentioned above? If you can, try looking at everything in the $_POST array to see what you’re getting from the form. Maybe the value you need is there but not located where you expect it to be. Your original code above looked correct, so I’m not sure why the response token isn’t being returned where you expect it to be. Now you know you’re actually getting the token you need, you’re getting close! :slight_smile:

Yes, $_POST is completely empty.
No clue so far as to why.
AFAICT that token should be in $_POST .

The $_POST populates AFTER clicking the form’s ‘Submit’.
And the token var is available as


The intended usage is to use CF Turnstile in a login form to verify a visitor, and block Submit until the captcha is successful.

To do that the token needs to be in $_POST before the Click on Submit.

Chicken and egg problem.

Cool. So, what happens with $_POST if the Turnstile div isn’t in the form at all? I mean if you’re not getting ANY form data, that would indicate something else is wrong. Unless you’re saying $_POST is empty only when the Turnstile div is in the form.

If you want to block the actual submit, I think you’ll need to do that on the client side via JavaScript. You can check to see if the response token was returned from Cloudflare and if it wasn’t block the form from being sent to the server. You can see some discussion about that here:

that thread is about using an invisible Turnstile widget, but the discussion about interrupting the form submission process still applies, in my opinion.

What you can do is create a small JavaScript function that’s “bound” to the “onSubmit” event on the form. Have that function return “true” to allow submission of the form and “false” to prevent submission.

Looks like I might be able to populate-then-grab the widget RESPONSE with

	$uniqId = mt_rand();
	$widgetId = 'cf-turnstile-c-' . $uniqId;
<html lang="en-US">
	<meta charset="UTF-8">
	<script src="" async defer>
/* ( render the widget ) */
		id="<?php echo $widgetId ?>"
function _tCb() {
	turnstile.getResponse(<?php echo $widgetId ?>)

Need to fuss with this a bit.

Nope :frowning_face:

turnstile.getResponse needs an arg of $widgetId

of the form ==


That widget ID is, again, available in the iframe … populate BY the CF implicit widget.
Which again I need to extract somehow.

More chickens. More eggs.
This is frustrating.

Try fetching the response when the form is submitted. I haven’t used the “onload” event on loading the Turnstile script but does that fire after the script has been loaded vs after the widget has been rendered? If you want to capture the response token before the form is submitted, you’ll need to grab it after the widget has been rendered. I still think you’ll need to do this on the browser side which will mean JavaScript. Let me try something and I’ll post a code snipet you can experiment with.

Ok, that was quick. lol I have some code to try:

<div class="cf-turnstile" data-sitekey="{put site key here}" data-callback="testCallBack"></div>

Then, put this at the bottom of your HTML:

        function testCallBack(responseData) {
            alert('testCallBack called. Response: ' + responseData);

If you get the response token, you can stash it and do something with it.

Huh. That returns a result in an alert!

I’m half-way through switching to explicit rendering from implicit thinking that might do something for me :-/
I’ll clean house, and stare at your snippet over a bowl of ramen.
Then see what I can manage!

Cool! So, one idea is this: put your own, hidden form field in your form to hold the response. Use JavaScript to capture the response sent from Cloudflare and save it in your form field. Then, see if that field is in the $_POST array when you submit the form.

It ends up in $_GET, not $_POST.

Not sure why yet.

But it’s there.

Now trying to DO something with it.

At the moment I have workflow that looks like

(1) render the client side widget
(2) it shows ‘Success’
(3) click Submit
(4) extract token
(5) serververify token with CF API
(6) if token OK, do stuff

I understand the need for (5)
I’m unclear why (2) is ever be displayed to a user as ‘Success’ if Submit-then(5) hasnt’ happened yet, and MIGHT return not-OK.

Great! Thanks for the update! I don’t know the internals of Turnstile but I think (assume) “Success” means the browser challenges were successful. So, as far as Turnstile is concerned, you’re a human. The need to verify the token is to protect against that token being forged by someone trying to abuse your form.

In any event, glad you were able to locate your form data!

By the way, I found this: php - Simple form not sending data via _POST - Stack Overflow

It’s old, but it might be relevant.


I’ve seen “every OTHER post on the 'net”, except that one.

And adding the name= attr fixes it.
Value’s now in $_POST, as you’d expect.
Good news is it’s starting to
Still trying to wrap head around the widget re-rendering – I assume with a 2nd API check – on server-verify.
Could possibly stop that with ajax, or ESI, but really not sure I care.

Groovy! Glad was of use to you! So, are things pretty much working now? Were you able to get your original code to work, as posted above? I mean not counting the form field change. :slight_smile:

I still can’t convince myself that I correctly wrapped the submit button conditionally.
I want to make sure that [on,after}-Submit actions only continue IF the CF server-verify comes back OK.

Other than that it seems to be working.
I’m working on removing useless code and cleaning up a bit.
But it’s starting to look like an easy-to-drop-in CF-preotected custom-login page with no plugin dependencies.

Thanks for the major assist!

1 Like

Cool! Once you get something working, could you post a stripped-down PHP sample here to contribute to the community here? :smiley:

Sure. Most seem to use plugins that seem to make a lot of assumptions that don’t fit for me. Maybe same for others.



“Cloudflare says it’s working on plugins for major platforms like WordPress to make Turnstile easier to deploy”

Don’t know what the status is or whether they’ll be any more generic than others.

1 Like