Skip to content
Coritan Docs

Move from Cloudflare Turnstile to Wicket

Change the script address, the widget's class name and the verify address to move a site from Turnstile to Wicket, and switch without a gap.

View as Markdown

Wicket speaks Cloudflare Turnstile's protocol, so a site that uses Turnstile moves by changing three things: the script address, the class name on the widget's container and the verify address. The form field and the JavaScript global change name too. The options, the callbacks and the answer your server reads keep their shape. This page lists each change, what stays the same, and an order to switch in that leaves no form unprotected.

  • Add a Wicket widget for the site, with the same hostnames as the Turnstile widget and the mode that matches (Add a widget). Turnstile's Managed, Non-Interactive and Invisible widgets match Wicket's Managed, Non-interactive and Invisible.
  • Copy the secret key when the page shows it. We show it once, and a lost key is replaced by rotating it (Rotate the secret key).
  • Find where your site loads the Turnstile script and where your server verifies a token. Search your code for turnstile and cf-turnstile.
What Turnstile Wicket
Script address https://challenges.cloudflare.com/turnstile/v0/api.js https://wicket.coritan.com/v1/api.js
Class on the container cf-turnstile wicket-widget
Verify address https://challenges.cloudflare.com/turnstile/v0/siteverify https://api.coritan.com/api/v1/wicket/siteverify
Form field cf-turnstile-response wicket-response
JavaScript global window.turnstile window.wicket
Site key Starts with 0x Starts with wk_
Secret key Starts with 0x Starts with wks_

The Install tab of your widget shows the exact script address and verify address for the widget (Install a widget).

A visitor who loaded a page before you changed it still holds a Turnstile token for as long as the page stays open, and a cache may keep serving the old page. Your server therefore verifies both kinds of token for an hour.

  1. Allow Wicket in your Content-Security-Policy, if your site sends one. Add https://wicket.coritan.com to script-src and frame-src, and keep Cloudflare's entries (Allow Wicket in a Content Security Policy).

  2. Change your server so it reads both fields. It verifies wicket-response at the Wicket address with the Wicket secret key, and cf-turnstile-response at Turnstile's address with the Turnstile secret key:

    JavaScript
    const wicketToken = form["wicket-response"];
    const turnstileToken = form["cf-turnstile-response"];
    
    const passed = wicketToken
      ? await verifyWithWicket(wicketToken)
      : await verifyWithTurnstile(turnstileToken);
    

    verifyWithWicket is the call in Verify a token on your server, and it posts to https://api.coritan.com/api/v1/wicket/siteverify:

    Verify a Wicket token
    curl https://api.coritan.com/api/v1/wicket/siteverify \
      --data-urlencode "secret=$WICKET_SECRET_KEY" \
      --data-urlencode "response=$TOKEN"
    
  3. Deploy the server, and check that forms that still use Turnstile are accepted.

  4. Change the page. Replace the script address, change the class from cf-turnstile to wicket-widget, and put the Wicket site key in data-sitekey:

    HTML
    <script src="https://wicket.coritan.com/v1/api.js" async defer></script>
    <div class="wicket-widget" data-sitekey="wk_..." style="min-height: 65px"></div>
    

    The min-height keeps the page from moving when the widget appears, and is 140px for a Compact widget. When your Content-Security-Policy forbids inline styles, put it in a stylesheet instead (Reserve the widget's height).

  5. If your own script calls turnstile.render, turnstile.reset or any other function, call wicket.render, wicket.reset and the others instead (Widget options).

  6. Load the page and submit the form. The widget should show Success, and the verify call should answer "success": true.

  7. Wait an hour. Then remove the Turnstile verify call, the Turnstile secret key and the Cloudflare entries in your Content-Security-Policy.

To test before step 4 goes live, use a test key on a development copy of the page. Turnstile's own test keys do not work with Wicket.

Tip

To keep your server reading cf-turnstile-response, set data-response-field-name="cf-turnstile-response" on the container. You still change the verify address. During the hour of overlap the two kinds of token would then arrive in the same field and your server could not tell them apart, so use wicket-response until Turnstile is gone.

What On Wicket
Widget options and data- attributes The same names and defaults, such as theme, size, language, action, cdata, appearance, execution, retry, refresh-expired and response-field-name. Wicket adds a few of its own (Widget options).
Callbacks callback, error-callback, expired-callback, timeout-callback, before-interactive-callback, after-interactive-callback and unsupported-callback.
Functions render, execute, reset, remove, getResponse, isExpired and ready.
The script's parameters render=explicit and onload.
The verify call's fields secret, response, remoteip and idempotency_key, sent form-encoded, as multipart form data or as JSON.
The verify answer success, challenge_ts, hostname, error-codes, action, cdata and metadata.
The error codes of the verify call missing-input-secret, invalid-input-secret, missing-input-response, invalid-input-response, bad-request, timeout-or-duplicate and internal-error.
The token It works once, for 300 seconds, and is at most 2048 characters.
idempotency_key A repeat of a call with the same key returns the first answer (Repeat a call safely).
Hostnames A hostname covers itself and every name under it, so example.com covers www.example.com.
What On Wicket
The secret key We show it once, when you add the widget and when you rotate it, and keep only a fingerprint. Copy it then.
Test keys Wicket has its own test site keys and secret keys, with their own names (Test keys).
The codes your error-callback receives Words such as network and failed, listed in Widget options.
metadata in the verify answer interactive and event. Store event to report a check later (Verify a token on your server).
The verify call's budget A widget may make 6000 calls a minute, and a call past it answers HTTP 429 with rate-limited (Limits).
Plan limits Your plan sets how many widgets you have and how many hostnames each lists (Limits).

Visitors pass the Wicket widget, and your server accepts forms on the strength of wicket-response alone. The Analytics tab of the widget counts the challenges, and Valid under Tokens verified rises with Passed (Read a widget's analytics).

The widget shows This site's check is not set up for this page with unknown_sitekey
The page still has the Turnstile site key. Put the widget's Wicket site key, which starts with wk_, in data-sitekey.
The widget does not appear
The container may still have the class cf-turnstile, or your Content-Security-Policy may block https://wicket.coritan.com. Troubleshoot Wicket lists the causes.
The verify call answers invalid-input-secret
The call sends the Turnstile secret key to the Wicket address. Send the widget's secret key, which starts with wks_.
The verify call answers invalid-input-response
The call sends a Turnstile token to the Wicket address. Read the token from wicket-response for Wicket and from cf-turnstile-response for Turnstile.
The console says turnstile is not defined
A script on your page still calls turnstile. Call wicket instead.