npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

wappler-cf-turnstile-server

v1.0.1

Published

Cloudflare Turnstile server-side verification module for Wappler Server Connect - Verify CAPTCHA tokens on the backend. Companion to wappler-cf-turnstile-client.

Readme

Cloudflare Turnstile Server-Side Verification

Server Connect module for Wappler that verifies Cloudflare Turnstile CAPTCHA tokens on the backend.

Description

This is the server-side companion to wappler-cf-turnstile-client. Use this module to verify Turnstile tokens received from the client and confirm that users are legitimate.

Features

  • Verify Turnstile tokens with Cloudflare API
  • Built-in error handling
  • Support for optional parameters (IP address, idempotency key)
  • Returns complete verification response
  • No external dependencies (uses Node.js built-in modules)
  • Timeout protection (10 seconds)
  • Detailed logging

Installation

Install this extension directly from Wappler:

  1. Open your Wappler project
  2. Go to Project SettingsExtensions
  3. Click Add Extension
  4. Search for wappler-cf-turnstile-server
  5. Click Install
  6. Restart Wappler

The "Verify Turnstile" action will appear in Server Connect under "Cloudflare Turnstile".

Usage

1. Get Your Secret Key

Get your Turnstile secret key from: https://dash.cloudflare.com/

Store it securely in environment variables:

# .env
TURNSTILE_SECRET_KEY=your-secret-key-here

2. Add to Server Connect

In Wappler Server Connect, you'll find the action under:

Cloudflare Turnstile > Verify Turnstile

3. Configure the Action

Required Parameters:

  • Secret Key: Your Cloudflare secret key
  • Turnstile Token: The token from the client

Optional Parameters:

  • Remote IP: User's IP address
  • Idempotency Key: Unique key to prevent duplicate verifications

Example Server Connect Action

{
  "meta": {
    "options": {
      "csrf": false
    }
  },
  "exec": {
    "steps": [
      {
        "name": "verifyTurnstile",
        "module": "turnstile",
        "action": "verify",
        "options": {
          "secret": "{{$_ENV.TURNSTILE_SECRET_KEY}}",
          "response": "{{$_POST['cf-turnstile-response']}}",
          "remoteip": "{{$_SERVER.REMOTE_ADDR}}"
        },
        "output": true
      },
      {
        "name": "checkVerification",
        "module": "core",
        "action": "condition",
        "options": {
          "if": "{{verifyTurnstile.success}}",
          "then": {
            "steps": [
              {
                "name": "processForm",
                "module": "dbupdater",
                "action": "insert",
                "options": {
                  "sql": {
                    "table": "submissions",
                    "values": [
                      {
                        "column": "email",
                        "value": "{{$_POST.email}}"
                      }
                    ]
                  }
                }
              }
            ]
          },
          "else": {
            "steps": [
              {
                "name": "returnError",
                "module": "core",
                "action": "response",
                "options": {
                  "status": 403,
                  "data": {
                    "error": "Verification failed",
                    "error_codes": "{{verifyTurnstile.error_codes}}"
                  }
                }
              }
            ]
          }
        }
      }
    ]
  }
}

Response Data

The verification action returns the following data:

| Property | Type | Description | |----------|------|-------------| | success | boolean | true if verification passed, false otherwise | | error_codes | array | Array of error codes if verification failed | | challenge_ts | string | ISO timestamp of the challenge | | hostname | string | Hostname where verification occurred | | action | string | Action value set in the client widget | | cdata | string | Custom data passed from the client |

Success Response Example

{
  "success": true,
  "error_codes": [],
  "challenge_ts": "2025-10-04T12:00:00.000Z",
  "hostname": "example.com",
  "action": "login",
  "cdata": null
}

Error Response Example

{
  "success": false,
  "error_codes": ["invalid-input-response"],
  "challenge_ts": null,
  "hostname": null
}

Common Error Codes

| Error Code | Description | |------------|-------------| | missing-input-secret | Secret key is missing | | invalid-input-secret | Secret key is invalid | | missing-input-response | Token is missing | | invalid-input-response | Token is invalid or has expired | | timeout-or-duplicate | Token has already been validated or has timed out | | internal-error | Internal error occurred |

Complete Workflow Example

Client-Side (using wappler-cf-turnstile-client)

<form id="contactForm" dmx-on:submit.prevent="submitContact.load()">
  <input type="email" name="email" placeholder="Email" required>
  <input type="text" name="message" placeholder="Message" required>
  
  <!-- Turnstile widget -->
  <dmx-turnstile 
    id="turnstile1" 
    sitekey="<%= process.env.TURNSTILE_SITEKEY %>">
  </dmx-turnstile>
  
  <!-- Token automatically included as hidden input: cf-turnstile-response -->
  
  <button type="submit" dmx-bind:disabled="!turnstile1.isVerified">
    Submit
  </button>
</form>

<dmx-serverconnect 
  id="submitContact" 
  url="/api/contact/submit"
  dmx-param:email="contactForm.email.value"
  dmx-param:message="contactForm.message.value"
  dmx-param:cf-turnstile-response="turnstile1.token">
</dmx-serverconnect>

Server-Side (/api/contact/submit.json)

{
  "exec": {
    "steps": [
      {
        "name": "verify",
        "module": "turnstile",
        "action": "verify",
        "options": {
          "secret": "{{$_ENV.TURNSTILE_SECRET_KEY}}",
          "response": "{{$_POST['cf-turnstile-response']}}",
          "remoteip": "{{$_SERVER.REMOTE_ADDR}}"
        },
        "output": true
      },
      {
        "name": "condition",
        "module": "core",
        "action": "condition",
        "options": {
          "if": "{{verify.success}}",
          "then": {
            "steps": [
              {
                "name": "saveMessage",
                "module": "dbupdater",
                "action": "insert",
                "options": {
                  "sql": {
                    "table": "messages",
                    "values": [
                      {"column": "email", "value": "{{$_POST.email}}"},
                      {"column": "message", "value": "{{$_POST.message}}"}
                    ]
                  }
                },
                "output": true
              }
            ]
          },
          "else": {
            "steps": [
              {
                "name": "error",
                "module": "core",
                "action": "response",
                "options": {
                  "status": 403,
                  "data": {
                    "error": "CAPTCHA verification failed"
                  }
                }
              }
            ]
          }
        }
      }
    ]
  }
}

Security Best Practices

1. Use Environment Variables

Never hardcode secret keys:

{
  "secret": "{{$_ENV.TURNSTILE_SECRET_KEY}}"
}

2. Always Verify on Server

Never trust client-side verification alone. Always verify tokens on your server.

3. Include IP Address

Pass the user's IP for additional security:

{
  "remoteip": "{{$_SERVER.REMOTE_ADDR}}"
}

4. Check Success Status

Always check the success property before processing requests:

{
  "if": "{{verify.success}}"
}

5. Handle Errors Gracefully

Return appropriate error responses:

{
  "status": 403,
  "data": {
    "error": "Verification failed",
    "code": "{{verify.error_codes[0]}}"
  }
}

Testing

Test Keys

Cloudflare provides test keys for development:

Always passes:

  • Site key: 1x00000000000000000000AA
  • Secret key: 1x0000000000000000000000000000000AA

Always fails:

  • Site key: 2x00000000000000000000AB
  • Secret key: 2x0000000000000000000000000000000AB

Example Test

{
  "name": "test",
  "module": "turnstile",
  "action": "verify",
  "options": {
    "secret": "1x0000000000000000000000000000000AA",
    "response": "test-token"
  },
  "output": true
}

Troubleshooting

Module Not Appearing

  1. Check extension is in project.json
  2. Verify files are in correct location
  3. Restart Wappler completely

"Invalid input secret" Error

  • Check secret key is correct
  • Verify environment variable is loaded
  • Ensure no extra spaces in key

"Invalid input response" Error

  • Token may have expired (tokens are valid for ~5 minutes)
  • Token may have already been used
  • Token may be from a different site key

Timeout Errors

  • Check network connection
  • Verify firewall isn't blocking Cloudflare API
  • Default timeout is 10 seconds

Requirements

  • Node.js >= 14.0.0
  • Wappler with Server Connect
  • Cloudflare Turnstile account

Related Packages

This extension requires the client-side companion:

  • wappler-cf-turnstile-client - Client-side Turnstile widget component
    • NPM: https://www.npmjs.com/package/wappler-cf-turnstile-client
    • GitHub: https://github.com/evaldas/wappler-cf-turnstile-client

Install both extensions for complete functionality:

npm install wappler-cf-turnstile-client
npm install wappler-cf-turnstile-server

Support

  • Wappler Community: https://community.wappler.io/
  • NPM: https://www.npmjs.com/package/wappler-cf-turnstile-server
  • GitHub: https://github.com/evaldas/wappler-cf-turnstile-server

License

MIT - See LICENSE file

Author

Evaldas Sedys


Version: 1.0.0
Last Updated: October 2025