# Cloudflare Turnstile Retry and Refresh-Expired Settings Explained

> What Cloudflare Turnstile's retry, retry-interval, refresh-expired and refresh-timeout settings do, their defaults, and when to change them.

- Source: https://zerocaptcha.io/blog/cloudflare-turnstile-retry-and-refresh
- Published: 2026-10-01
- Author: ZeroCaptcha Engineering

A Cloudflare Turnstile widget has four settings that decide what it does after something goes
wrong: **`retry`** (`auto` by default, or `never`) retries a failed challenge by itself;
**`retry-interval`** sets the wait between those retries, **8,000 ms** by default and 900,000 ms at
most; **`refresh-expired`** (`auto` by default, `manual` or `never`) decides what happens when the
token expires after **300 seconds**; and **`refresh-timeout`** (`auto` by default in Managed mode,
`manual` or `never`) decides what happens when an interactive challenge is not completed in time.
Each can be set as a `data-` attribute or as a `turnstile.render` option.

This explainer describes each one with Cloudflare's wording, as checked on 1 October 2026, shows a
widget that handles failure and expiry itself, and says what the settings mean for automated
clients.

## The four settings

| `turnstile.render` option | Data attribute | Values | Default | What it controls |
| --- | --- | --- | --- | --- |
| `retry` | `data-retry` | `auto`, `never` | `auto` | "Controls whether the widget should automatically retry to obtain a token" |
| `retry-interval` | `data-retry-interval` | milliseconds, up to 900,000 | 8,000 | "the time between retry attempts" |
| `refresh-expired` | `data-refresh-expired` | `auto`, `manual`, `never` | `auto` | What happens when the token expires |
| `refresh-timeout` | `data-refresh-timeout` | `auto`, `manual`, `never` | `auto` in Managed mode | What happens when an interactive challenge times out |

Cloudflare describes the values like this:

- **`retry: auto`** "Automatically retries failed challenges. Auto retry provides better visitor
  experience by automatically recovering from temporary network issues or processing errors."
- **`retry: never`** "Disables automatic retry. This requires manual intervention and gives you
  full control over error handling."
- **`refresh-expired: auto`** refreshes expired tokens automatically; **`manual`** "gives visitors
  control but requires them to take action"; **`never`** leaves all refresh logic to your page.
- **`refresh-timeout: auto`** "Automatically refreshes upon encountering an interactive timeout";
  **`manual`** "Prompts the visitor to manually refresh"; **`never`** "Will show a timeout".

## The callbacks that go with them

| Option | Cloudflare's description |
| --- | --- |
| `callback` | "A JavaScript callback invoked upon success of the challenge" |
| `error-callback` | "A JavaScript callback invoked when there is an error" |
| `expired-callback` | "Invoked when the token expires and does not reset the widget" |
| `timeout-callback` | "Invoked when the challenge presents an interactive challenge but was not solved within a given time" |

And the widget's methods: `turnstile.reset(widgetId)`, "To reset the widget if the given widget
timed out or expired"; `turnstile.getResponse(widgetId)` to read the current token;
`turnstile.isExpired(widgetId)` to check it; and `turnstile.remove(widgetId)`.

## When to change the defaults

The defaults suit most forms: failures retry on their own, and a visitor who leaves a form open for
more than five minutes gets a new token without noticing. Change them when your page needs control:

- **`retry: never`** when you show your own error message and let the visitor try again, or when
  you log every failure before retrying. Pair it with `error-callback`.
- **A longer `retry-interval`** when many failures in a row would only add load, such as on a page
  behind a slow network.
- **`refresh-expired: manual`** when a silent refresh would surprise the visitor, for example if you
  disable the submit button while the widget works.
- **`refresh-expired: never`** when your page manages tokens itself: it hears `expired-callback`
  and calls `turnstile.reset()` when it is ready.

Here is a widget, rendered explicitly (the script loaded with `?render=explicit`), that shows its
own error message, and refreshes an expired token only when the visitor submits:

```js
const submit = document.querySelector("#signup button[type=submit]");
const message = document.querySelector("#signup .captcha-message");

const widgetId = turnstile.render("#signup .captcha", {
  sitekey: "0x4AAAAAAAB1cD2eF3gH4iJ5",
  action: "signup",
  retry: "never",
  "refresh-expired": "never",
  callback: () => {
    message.textContent = "";
    submit.disabled = false;
  },
  "error-callback": (code) => {
    submit.disabled = true;
    message.textContent = `The security check failed (${code}). Press "Try again".`;
  },
  "expired-callback": () => {
    submit.disabled = true;
    message.textContent = "The security check expired. It will run again when you submit.";
  },
});

document.querySelector("#signup .captcha-retry").addEventListener("click", () => {
  turnstile.reset(widgetId);
});

document.querySelector("#signup").addEventListener("submit", (event) => {
  if (turnstile.isExpired(widgetId) || !turnstile.getResponse(widgetId)) {
    event.preventDefault();
    turnstile.reset(widgetId);
  }
});
```

The error codes your `error-callback` receives are listed in
[Cloudflare Turnstile error codes](https://zerocaptcha.io/blog/cloudflare-turnstile-error-codes). Whatever the widget
does, your server must still validate each token with siteverify: the settings above only change
the page.

## What the settings do not change

- **The token's lifetime.** "Tokens are single-use and expire after 300 seconds (five minutes)."
  `refresh-expired: auto` gets a new token; it never makes an old one last longer. See
  [Cloudflare Turnstile token expiry](https://zerocaptcha.io/guides/cloudflare-turnstile-token-expiry).
- **Server-side checks.** A retried or refreshed token is a new token, validated once like any
  other. A replayed one fails with `timeout-or-duplicate`.
- **The widget's mode.** Managed, Non-Interactive and Invisible are set on the widget in the
  Cloudflare dashboard; see [Cloudflare Turnstile widget modes](https://zerocaptcha.io/guides/cloudflare-turnstile-widget-modes).

## What they mean for automated clients

When your automation puts a token from a solving API into a page, the page's widget settings
still run around it:

- **Your token has its own 300 seconds.** The widget does not know when it was issued, and its
  refresh settings do not extend it. Fill the field just before you submit.
- **Pages that gate the button on callbacks** (as in the example above) need the `callback` called
  with your token, or the button stays disabled. [Submit a Cloudflare Turnstile token](https://zerocaptcha.io/guides/submit-cloudflare-turnstile-token)
  shows how to find and call it.
- **A widget that keeps retrying** in an automated browser, with `retry: auto`, can change the page
  while you work. Read the sitekey once, then write the token and submit in one step.

ZeroCaptcha returns a token for the page and sitekey you name, valid for 300 seconds and good for
one submission; the [Cloudflare Turnstile solver](https://zerocaptcha.io/cloudflare-turnstile-solver) page shows the call
in ten languages and tools.

## Sources

- [Cloudflare Turnstile: widget configurations](https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/widget-configurations/)
  (checked 1 October 2026).
- [Cloudflare Turnstile: embed the widget](https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/),
  for explicit rendering and the `turnstile` methods (checked 1 October 2026).
- [Cloudflare Turnstile: server-side validation](https://developers.cloudflare.com/turnstile/get-started/server-side-validation/)
  (checked 1 October 2026).

## Questions

### What does refresh-expired do in Cloudflare Turnstile?

It decides what the widget does when its token expires after 300 seconds: auto (the default) refreshes it automatically, manual asks the visitor to refresh, and never leaves all refresh logic to your page.

### What is the default retry-interval of Cloudflare Turnstile?

8,000 milliseconds. It sets the time between automatic retries after a failed challenge, up to a maximum of 900,000 milliseconds, and only applies while retry is auto.

### Does refresh-expired make a Cloudflare Turnstile token last longer?

No. Every token expires 300 seconds after it was generated and can be validated once. Refreshing gets the widget a new token; it never extends an old one.
