Help with server-side validation, not understanding where the pieces need to go

Before asking, did you search first? Press :mag: at the upper right to search.

Hello,

I have tried a lot of different things to get this to work but I feel like I’m going in circles.

I have this is the header

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

and this is the button

<!-- Submit with reCAPTCHA -->
          <div class="cfcapwrap">
            <script type="text/javascript">
              window.onloadTurnstileCallback = function () {
                turnstile.render('cfcaptcha', {
                  sitekey: '0x4AAAAAAAYFWTfOh7mWLOXG',
                  callback: function(token) {
                    console.log(`Challenge Success ${token}`);
                  },
                });
              };

              // if using synchronous loading, will be called once the DOM is ready
              turnstile.ready(onloadTurnstileCallback);
            </script>

            <div class="cf-turnstile cfcaptcha" id="cfcaptcha" data-sitekey="0x4AAAAAAAYFWTfOh7mWLOXG" data-callback="javascriptCallback"></div>
          </div>
          <div class="submitContact">
            <button type="submit" class="submitbtn" tabindex="12"

            >Send Message!</button>


          </div>


        </form>

I’ve added this to the end of the page

<!-- reCAPTCHA Script -->

<script src="/js/reCAPTCHAv2.js"></script>

<script src="/js/cfCAPTCHA.js"></script>

and below is for reCAPTCHAv2.js


// This is the demo secret key. In prod, we recommend you store
// your secret key(s) safely.
const SECRET_KEY = 'My Secret Key';

async function handlePost(request) {
  const body = await request.formData();
  // Turnstile injects a token in "cf-turnstile-response".
  const token = body.get('cf-turnstile-response');
  const ip = request.headers.get('CF-Connecting-IP');

  // Validate the token by calling the "/siteverify" API.
  let formData = new FormData();
  formData.append('secret', SECRET_KEY);
  formData.append('response', token);
  formData.append('remoteip', ip);

  const result = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
    body: formData,
    method: 'POST',
  });

  const outcome = await result.json();
  if (!outcome.success) {
    return new Response('The provided Turnstile token was not valid! \n' + JSON.stringify(outcome));
  }
  // The Turnstile token was successfully validated. Proceed with your application logic.
  // Validate login, redirect user, etc.
  // For this demo, we just echo the "/siteverify" response:
  return new Response('Turnstile token successfully validated. \n' + JSON.stringify(outcome));
}

And below is cfCAPTCHA.js


window.onloadTurnstileCallback = function () {
  turnstile.render('#example-container', {
    sitekey: '0x4AAAAAAAYFWTfOh7mWLOXG',
    callback: function(token) {
      console.log(`Challenge Success ${token}`);
    },
  });
};

I’m completely lost. The widget appears and works. But even if the widget is still verifying or failed I can submit the form anyways.

I’m a huge noob to this so go easy on me :sweat_smile:

I’m happy to provide any additional details.

Thank you