diff --git a/public/__redirects b/public/__redirects index b833bd017d08d24..afe72179b0d686e 100644 --- a/public/__redirects +++ b/public/__redirects @@ -1549,6 +1549,16 @@ /turnstile/concepts/widget-types/ /turnstile/concepts/widget/ 301 /turnstile/concepts/domain-management/ /turnstile/concepts/hostname-management/ 301 /turnstile/troubleshooting/challenge-solve-issues/ /cloudflare-challenges/troubleshooting/challenge-solve-issues/ 301 +/turnstile/concepts/ephemeral-id/ /turnstile/additional-configuration/ephemeral-id/ 301 +/turnstile/concepts/hostname-management/ /turnstile/additional-configuration/hostname-management/ 301 +/turnstile/concepts/pre-clearance-support/ /cloudflare-challenges/concepts/clearance/ 301 +/turnstile/api-reference/ /turnstile/get-started/widget-management/api/ 301 +/turnstile/concepts/appearance-modes/ /turnstile/get-started/client-side-rendering/widget-configurations/ 301 +/turnstile/demos/ /turnstile/tutorials/ 301 +/turnstile/get-started/pre-clearance/ /cloudflare-challenges/concepts/clearance/ 301 +/turnstile/get-started/terraform/ /turnstile/get-started/widget-management/terraform/ 301 +/turnstile/glossary/ /turnstile/ 301 +/turnstile/get-started/supported-browsers/ /cloudflare-challenges/reference/supported-browsers/ 301 # waf /waf/about/ /waf/concepts/ 301 diff --git a/src/content/docs/cloudflare-challenges/concepts/clearance.mdx b/src/content/docs/cloudflare-challenges/concepts/clearance.mdx index dd6a724b882af8d..e98a24807155ee8 100644 --- a/src/content/docs/cloudflare-challenges/concepts/clearance.mdx +++ b/src/content/docs/cloudflare-challenges/concepts/clearance.mdx @@ -64,8 +64,31 @@ Allows a user with a clearance cookie to not be challenged by JavaScript Challen ### Clearance cookie duration -Clearance cookies generated by the Turnstile widget will be valid for the time specified by the zone-level Challenge Passage value. To configure the Challenge Passage setting, refer to the [Cloudflare Challenges documentation](/cloudflare-challenges/challenge-types/challenge-pages/challenge-passage/). +Clearance cookies generated by the Turnstile widget will be valid for the time specified by the zone-level Challenge Passage value. To configure the Challenge Passage setting, refer to [Challenge Passage](/cloudflare-challenges/challenge-types/challenge-pages/challenge-passage/). ### Setup -To set up pre-clearance cookies for Turnstile, refer to [Enable pre-clearance cookies](/turnstile/get-started/pre-clearance/). +To enable pre-clearance, you must ensure that the hostname of the Turnstile widget matches the zone with the WAF rules. During the Turnstile configuration setup in the Cloudflare dashboard, you have access to a list of registered zones. Select the appropriate hostname from this list. + +The prerequisite is crucial for pre-clearance to function properly. If set up correctly, visitors who successfully solve Turnstile will receive a cookie with the security clearance level set by the customer. When encountering a WAF challenge on the same zone, they will bypass additional challenges for the configured clearance level and below. + +For more details on managing hostnames, refer to the [Hostname Management documentation](/turnstile/additional-configuration/hostname-management/). + + + +#### Enable pre-clearance on a new site + +1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/login) and select your account. +2. Go to **Turnstile** > **Add widget**. +3. Under **Would you like to opt for pre-clearance for this site?** select **Yes**. +4. Choose the pre-clearance level from the select box. +5. Select **Create**. + +#### Enable pre-clearance on an existing site + +1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/login) and select your account. +2. Go to **Turnstile**. +3. Go to the existing widget or site and select **Settings**. +4. Under **Would you like to opt for pre-clearance for this site?** select **Yes**. +5. Choose the pre-clearance level from the select box. +6. Select **Update**. \ No newline at end of file diff --git a/src/content/docs/cloudflare-challenges/frequently-asked-questions.mdx b/src/content/docs/cloudflare-challenges/frequently-asked-questions.mdx index 2fa3c16d5f51e7a..59e63e302130636 100644 --- a/src/content/docs/cloudflare-challenges/frequently-asked-questions.mdx +++ b/src/content/docs/cloudflare-challenges/frequently-asked-questions.mdx @@ -85,7 +85,7 @@ Block Amazon Web Services (AWS) and Google Cloud Platform (GCP) because of large Previously, unless you customize your front-end application, any AJAX request that is challenged will fail because AJAX calls are not rendered in the DOM. -Now, you can [opt-in to Turnstile's Pre-clearance cookies](/turnstile/concepts/pre-clearance-support/). This allows you to issue a Challenge early in your web application flow and pre-clear users to interact with sensitive APIs. Clearance cookies issued by a Turnstile widget are automatically applied to the Cloudflare zone that the Turnstile widget is embedded on, with no configuration necessary. The duration of the clearance cookie's validity is controlled by the zone-specific configurable [Challenge Passage](/cloudflare-challenges/challenge-types/challenge-pages/challenge-passage/) security setting. +Now, you can [opt-in to Turnstile's Pre-clearance cookies](/cloudflare-challenges/concepts/clearance/#pre-clearance-support-in-turnstile). This allows you to issue a Challenge early in your web application flow and pre-clear users to interact with sensitive APIs. Clearance cookies issued by a Turnstile widget are automatically applied to the Cloudflare zone that the Turnstile widget is embedded on, with no configuration necessary. The duration of the clearance cookie's validity is controlled by the zone-specific configurable [Challenge Passage](/cloudflare-challenges/challenge-types/challenge-pages/challenge-passage/) security setting. ## Why would I not find any failed Challenges? diff --git a/src/content/docs/turnstile/concepts/ephemeral-id.mdx b/src/content/docs/turnstile/additional-configuration/ephemeral-id.mdx similarity index 100% rename from src/content/docs/turnstile/concepts/ephemeral-id.mdx rename to src/content/docs/turnstile/additional-configuration/ephemeral-id.mdx diff --git a/src/content/docs/turnstile/concepts/hostname-management.mdx b/src/content/docs/turnstile/additional-configuration/hostname-management.mdx similarity index 100% rename from src/content/docs/turnstile/concepts/hostname-management.mdx rename to src/content/docs/turnstile/additional-configuration/hostname-management.mdx diff --git a/src/content/docs/turnstile/additional-configuration/index.mdx b/src/content/docs/turnstile/additional-configuration/index.mdx new file mode 100644 index 000000000000000..21c73db956c5b18 --- /dev/null +++ b/src/content/docs/turnstile/additional-configuration/index.mdx @@ -0,0 +1,15 @@ +--- +title: Additional configurations +pcx_content_type: navigation +sidebar: + order: 5 + group: + hideIndex: true + +--- + +import { DirectoryListing } from "~/components" + +Refer to the following pages for more information about Turnstile's additional configurations: + + diff --git a/src/content/docs/turnstile/concepts/pre-clearance-support.mdx b/src/content/docs/turnstile/additional-configuration/pre-clearance-support.mdx similarity index 100% rename from src/content/docs/turnstile/concepts/pre-clearance-support.mdx rename to src/content/docs/turnstile/additional-configuration/pre-clearance-support.mdx diff --git a/src/content/docs/turnstile/api-reference.mdx b/src/content/docs/turnstile/api-reference.mdx deleted file mode 100644 index f5835f4e6aed0fd..000000000000000 --- a/src/content/docs/turnstile/api-reference.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -pcx_content_type: navigation -title: API reference -external_link: /api/resources/turnstile/subresources/widgets/methods/list/ -sidebar: - order: 12 - ---- diff --git a/src/content/docs/turnstile/concepts/appearance-modes.mdx b/src/content/docs/turnstile/concepts/appearance-modes.mdx deleted file mode 100644 index 6b162fadc487a88..000000000000000 --- a/src/content/docs/turnstile/concepts/appearance-modes.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Appearance modes -pcx_content_type: reference ---- - - -``` - - - -
- -```javascript title="Related code" -window.onloadTurnstileCallback = function () { - turnstile.render("#example-container", { - sitekey: "", - callback: function (token) { - console.log(`Challenge Success ${token}`); - }, - }); -}; -``` - -
- -Or: - -
- -```html title="JavaScript tag" - -``` - -
- -
- -```javascript title="Related code" -// if using synchronous loading, will be called once the DOM is ready -turnstile.ready(function () { - turnstile.render("#example-container", { - sitekey: "", - callback: function (token) { - console.log(`Challenge Success ${token}`); - }, - }); -}); -``` - -
- -Turnstile can be programmatically loaded by invoking the `turnstile.render()` function in the global `window` object. - -The `turnstile.render: function (container: string | HTMLElement, params: RenderParameters)` render takes an argument to a HTML widget. - -If the invocation is successful, the function returns a `widgetId (string)`. If the invocation is unsuccessful, the function returns `undefined`. - -## Access a widget's state - -In addition to the `render()` function, Turnstile supports obtaining the widget's response from a `widgetId` via the `turnstile.getResponse(widgetId: string)` function. If you omit the `widgetId`, `turnstile.getResponse()` returns the response from the last created widget. - -After some time, a widget may become expired and needs to be refreshed (by calling `turnstile.reset(widgetId: string)`). If a widget has expired, `turnstile.getResponse()` will still return the last response, but the response will no longer be valid because it has expired. - -You can check if a widget has expired by either subscribing to the `expired-callback` or using the `turnstile.isExpired(widgetId: string)` function, which returns `true` if the widget is expired. If you omit `widgetId`, `turnstile.isExpired()` returns whether the last created widget is expired or not. - -## Reset a widget - -If a given widget has timed out, expired or needs to be reloaded, you can use the `turnstile.reset(widgetId: string)` function to reset the widget. - -## Remove a widget - -Once a widget is no longer needed, it can be removed from the page using `turnstile.remove(widgetId: string)`. This will not call any callback and will remove all related DOM elements. - -To unmount Turnstile, `turnstile.render()` will return an ID which you can pass to `turnstile.remove()`. - -## Refresh an expired token - -A few seconds before a token expires, the `expired-callback` is invoked. - -The `refresh-expired` or `data-refresh-expired` parameter defines the behavior when the token of a Turnstile widget has expired. - -By default, the parameter is set to `auto`, which will automatically instruct Turnstile to obtain a new token by rerunning the challenge. After the challenge is solved again, the `callback`, if specified, is invoked with the new token. - -The visitor can also be instructed to manually obtain a new token by setting the `refresh-expired` parameter to `manual`. - -Additionally, specifying `never` will not result in a regeneration of a token, and the application using Turnstile will be responsible for obtaining a novel Turnstile token. - -## Refresh a timed-out widget - -When managed mode is chosen, Turnstile may present the visitor with an interactive challenge at times. If this interactive challenge is presented but was not solved within a given time period, it will time out and Turnstile's challenge process will need to be restarted. - -The `refresh-timeout` or `data-refresh-timeout` parameter defines the behavior when the interactive challenge encounters a timeout. By default, the widget automatically refreshes (`auto`). However, the widget can also be configured such that the visitor needs to manually refresh a timed-out widget (`manual`), or the widget can only refreshed externally (`refresh-timeout="never"`) by the application (e.g. by calling Turnstile's `reset()` function). - -When a widget is encountering the interactivity timeout the `timeout-callback` is invoked. - -## Execution modes - -By default, Turnstile tokens are obtained for a visitor upon the rendering of a widget (even in invisible mode). However, in some scenarios, an application may want to embed Turnstile, but defer running the challenge until a certain point in time. This is where execution mode can be used to control when a challenge runs and a token is being generated. - -There are two options: - -- The challenge runs automatically after calling the `render()` function. -- The challenge runs after the `render()` function has been called, by invoking the `turnstile.execute(container: string | HTMLElement, jsParams?: RenderParameters)` function separately. - - This detaches the appearance and rendering of a widget from its execution. - -## Appearance modes - -If a widget is visible, its appearance can be controlled via the `appearance` parameter in three different ways: - -|
Mode
| Description | -| --- | --- | -| `always`
(Default) | `appearance` is set to `always` for visible widget types. | -| `interaction-only` | The widget will become visible in cases where an interaction is required. | -| `execute` | The widget will become visible after the challenge begins. This is helpful in situations where `execute()` is called after `render()`. | - -Refer to [Widget types](/turnstile/concepts/widget/) to see widgets by type and state. - -## Widget size - -The Turnstile widget can have two different fixed sizes or a flexible width size when using the Managed or Non-interactive modes: - - - -```html title="Flexible width example" -
-
-
-``` - -```html title="Compact mode example" -
-
-
-``` - -## Configurations - -| JavaScript Render Parameters | Data Attribute | Description | -| ----------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `sitekey` | `data-sitekey` | Every widget has a sitekey. This sitekey is associated with the corresponding widget configuration and is created upon the widget creation. | -| `action` | `data-action` | A customer value that can be used to differentiate widgets under the same sitekey in analytics and which is returned upon validation. This can only contain up to 32 alphanumeric characters including `_` and `-`. | -| `cData` | `data-cdata` | A customer payload that can be used to attach customer data to the challenge throughout its issuance and which is returned upon validation. This can only contain up to 255 alphanumeric characters including `_` and `-`. | -| `callback` | `data-callback` | A JavaScript callback invoked upon success of the challenge. The callback is passed a token that can be validated. | -| `error-callback` | `data-error-callback` | A JavaScript callback invoked when there is an error (e.g. network error or the challenge failed). Refer to [Client-side errors](/turnstile/troubleshooting/client-side-errors/). | -| `execution` | `data-execution` | Execution controls when to obtain the token of the widget and can be on `render` (default) or on `execute`. Refer to [Execution Modes](/turnstile/get-started/client-side-rendering/#execution-modes) for more information. | -| `expired-callback` | `data-expired-callback` | A JavaScript callback invoked when the token expires and does not reset the widget. | -| `before-interactive-callback` | `data-before-interactive-callback` | A JavaScript callback invoked before the challenge enters interactive mode. | -| `after-interactive-callback` | `data-after-interactive-callback` | A JavaScript callback invoked when challenge has left interactive mode. | -| `unsupported-callback` | `data-unsupported-callback` | A JavaScript callback invoked when a given client/browser is not supported by Turnstile. | -| `theme` | `data-theme` | The widget theme. Can take the following values: `light`, `dark`, `auto`.

The default is `auto`, which respects the user preference. This can be forced to light or dark by setting the theme accordingly. | -| `language` | `data-language` | Language to display, must be either: `auto` (default) to use the language that the visitor has chosen, or an ISO 639-1 two-letter language code (e.g. `en`) or language and country code (e.g. `en-US`). Refer to the [list of supported languages](/turnstile/reference/supported-languages/) for more information. | -| `tabindex` | `data-tabindex` | The tabindex of Turnstile's iframe for accessibility purposes. The default value is `0`. | -| `timeout-callback` | `data-timeout-callback` | A JavaScript callback invoked when the challenge presents an interactive challenge but was not solved within a given time. A callback will reset the widget to allow a visitor to solve the challenge again. | -| `response-field` | `data-response-field` | A boolean that controls if an input element with the response token is created, defaults to `true`. | -| `response-field-name` | `data-response-field-name` | Name of the input element, defaults to `cf-turnstile-response`. | -| `size` | `data-size` | The widget size. Can take the following values: `normal`, `flexible`, `compact`. | -| `retry` |  `data-retry` | Controls whether the widget should automatically retry to obtain a token if it did not succeed. The default is `auto`, which will retry automatically. This can be set to `never` to disable retry on failure. | -| `retry-interval` |  `data-retry-interval` | When `retry` is set to `auto`, `retry-interval` controls the time between retry attempts in milliseconds. Value must be a positive integer less than `900000`, defaults to `8000`. | -| `refresh-expired` |  `data-refresh-expired` | Automatically refreshes the token when it expires. Can take `auto`, `manual`, or `never`, defaults to `auto`. | -| `refresh-timeout` | `data-refresh-timeout` | Controls whether the widget should automatically refresh upon entering an interactive challenge and observing a timeout. Can take `auto` (automatically refreshes upon encountering an interactive timeout), `manual` (prompts the visitor to manually refresh) or `never` (will show a timeout), defaults to `auto`. Only applies to widgets of mode managed. | -| `appearance` | `data-appearance` | Appearance controls when the widget is visible. It can be `always` (default), `execute`, or `interaction-only`. Refer to [Appearance modes](/turnstile/get-started/client-side-rendering/#appearance-modes) for more information. | -| `feedback-enabled` | `data-feedback-enabled` | Allows Cloudflare to gather visitor feedback upon widget failure. It can be `true` (default) or `false`. | diff --git a/src/content/docs/turnstile/get-started/client-side-rendering/index.mdx b/src/content/docs/turnstile/get-started/client-side-rendering/index.mdx new file mode 100644 index 000000000000000..2efd3d2ddb91021 --- /dev/null +++ b/src/content/docs/turnstile/get-started/client-side-rendering/index.mdx @@ -0,0 +1,420 @@ +--- +title: Embed the widget +pcx_content_type: get-started +sidebar: + order: 3 +--- + +import { Render, Tabs, TabItem, Steps, Details } from "~/components"; + +Learn how to add the Turnstile widget to your webpage using implicit or explicit rendering methods. + +## Prerequisites + +Before you begin, you must have: + +- A Cloudflare account +- [A Turnstile widget](/turnstile/get-started/#1-create-your-widget) with a sitekey +- Access to edit your website's HTML +- Basic knowledge of HTML and JavaScript + +## Process + +1. Page load: The Turnstile script loads and scans for elements or waits for programmatic calls. +2. Widget rendering: Widgets are created and begin running challenges. +3. Token generation: When a challenge is completed, a token is generated. +4. Form integration: The token is made available via callbacks or hidden form fields. +5. Server validation: Your server receives the token and validates it using the Siteverify API. + +## Implicit rendering + +Implicit rendering automatically scans your HTML for elements with the `cf-turnstile` class and renders widgets when the page loads. + +### Use cases + +Cloudflare recommends using implicit rendering on the following scenarios: + +- You have simple implementations and want a quick integration. +- You have static websites with straightforward forms. +- You want widgets to appear immediately on pageload. +- You do not need programmatic control of the widget. + +### Implementation + +#### 1. Add the Turnstile script + +Add the Turnstile JavaScript API to your HTML. + +```html wrap + +``` + +:::caution +The `api.js` file must be fetched from the exact URL shown above. Proxying or caching this file will cause Turnstile to fail when future updates are released. +::: + +#### 2. Add widget elements + +Add widget containers where you want the challenges to appear on your website. + +```html wrap +
+``` + +#### 3. Configure with data attributes + +[Customize your widgets](/turnstile/get-started/client-side-rendering/widget-configurations/) using data attributes. + +```html +
+
+``` + +### Complete implicit rendering examples by use case + +
+```html title="Example" + + + + Login Form + + + +
+ + + + +
+ +
+ + +``` +
+ +
+ +```html title="Example" + +
+ + + +
+ +
+ + +``` +
+ +
+ +```html title="Example" + +
+ +
+ +
+ + +
+ + + +
+ +
+``` +
+ +
+ +When you embed a Turnstile widget inside a `
` element, an invisible input field with the name `cf-turnstile-response` is automatically created. This field contains the verification token and gets submitted with your other form data. + +```html + + +
+ + + +
+``` +
+ +--- + +## Explicit rendering + +Explicit rendering gives you programmatic control over when and how widgets are created using JavaScript functions. + +### Use cases + +Cloudflare recommends using explicit rendering on the following scenarios: + +- You have dynamic websites and single-page applications (SPAs). +- You need to control the timing of widget creation. +- You want to conditionally render the widget based on visitor interactions. +- You want multiple widgets with different configurations. +- You have complex applications requiring widget lifecycle management. + +### Implementation + +#### 1. Add the script to your website with explicit rendering + +```html wrap + +``` + +#### 2. Create container elements + +Create containers without the `cf-turnstile` class. + +```html +
+``` + +#### 3. Render the widgets programmatically + +Call `turnstile.render()` when you are ready to create the widget. + +```js +const widgetId = turnstile.render('#turnstile-container', { + sitekey: '', + callback: function(token) { + console.log('Success:', token); + } +}); +``` + +### Complete explicit rendering examples by use case + +
+ +```html title="Example" + + + + Explicit Rendering + + + +
+ + +
+ +
+ + + + +``` +
+ +
+ +```html title="Example" + +
+ +``` +
+ +
+ +```html title="Example" +
+ + + + +``` +
+ +### Widget lifecycle management + +Explicit rendering provides full control over the widget lifecycle. + +```js +// Render a widget +const widgetId = turnstile.render('#container', { + sitekey: '', + callback: handleSuccess +}); + +// Get the current token +const token = turnstile.getResponse(widgetId); + +// Check if widget is expired +const isExpired = turnstile.isExpired(widgetId); + +// Reset the widget (clears current state) +turnstile.reset(widgetId); + +// Remove the widget completely +turnstile.remove(widgetId); +``` + +### Execution mode + +Control when challenges run with execution modes. + +```js +// Render widget but don't run challenge yet +const widgetId = turnstile.render('#container', { + sitekey: '', + execution: 'execute' // Don't auto-execute +}); + +// Later, run the challenge when needed +turnstile.execute('#container'); +``` + +--- + +## Configuration options + +Both implicit and explicit rendering methods support the same configuration options. Refer to the table below for the most commonly used configurations. + +| Option | Description | Values | +| --- | --- | --- | +| `sitekey` | Your widget's sitekey | Required string | +| `theme` | Visual theme | `auto`, `light`, `dark` | +| `size` | Widget size | `normal`, `flexible`, `compact` | +| `callback` | Success callback | Function | +| `error-callback` | Error callback | Function | +| `execution` | When to run the challenge | `render`, `execute` | +| `appearance` | When the widget is visible | `always`, `execute`, `interaction-only` | + +For a complete list of configuration options, refer to [Widget configurations](/turnstile/get-started/client-side-rendering/widget-configurations/). + +--- + +## Testing + + + +--- + +## Security requirements + + diff --git a/src/content/docs/turnstile/get-started/client-side-rendering/widget-configurations.mdx b/src/content/docs/turnstile/get-started/client-side-rendering/widget-configurations.mdx new file mode 100644 index 000000000000000..2061bcb05cfda93 --- /dev/null +++ b/src/content/docs/turnstile/get-started/client-side-rendering/widget-configurations.mdx @@ -0,0 +1,501 @@ +--- +title: Widget configurations +pcx_content_type: reference +sidebar: + order: 5 +--- + +import { Render, Tabs, TabItem, Steps, Details } from '~/components'; + +Configure your Turnstile widget's appearance, behavior, and functionality using data attributes or JavaScript render parameters. + +## Rendering methods + +Turnstile widgets can be implemented using implicit or explicit rendering. + + + + Implicit rendering automatically scans your HTML for elements with the `cf-turnstile` class and renders the widget when the page loads. It is best used for simple implementations, static websites, or when you want widgets to appear immediately on page load. + + **How it works** + + 1. Add the Turnstile script to your page. + 2. Include `
` elements. + 3. Widgets will render automatically when the page loads. + 4. Configure the widget using `data-*` attributes on the HTML element. +
+ + ```html title="Example" wrap + +
+ ``` +
+ + Explicit rendering gives you programmatic control over when and how widgets are created using JavaScript functions. It is best used for dynamic websites and single-page applications (SPAs), when you need to control timing of widget creation, conditional rendering based on visitor interactions, or for multiple widgets with different configurations. + + **How it works** + + 1. Add the Turnstile script with `?render=explicit` parameter. + 2. Create container elements (without the `cf-turnstile` class). + 3. Call `turnstile.render()` function when you want to create widgets. + 4. Configure the widget using JavaScript object parameters. + + + ```html title="Example" wrap + +
+ + + ``` +
+
+ +--- + +## Widget sizes + +The Turnstile widget can have two different fixed sizes or a flexible width size when using the Managed or Non-Interactive modes. + +| Size | Width | Height | Use case | +| ------- | ----- | ------ | ------ | +| Normal | 300px | 65px | Standard implementation | +| Flexible | 100% (min: 300px) | 65px | Responsive design | +| Compact | 150px | 140px | Space-constrained layouts | + +- `normal`: The default size works well for most desktop and mobile layouts. Use this if you have adequate horizontal space on your website or form. +- `flexible`: Automatically adapts to the container width while maintaining minimum usability. Use this for responsive designs that need to work across all screen sizes. +- `compact`: Ideal for mobile interfaces, sidebars, or any space where horizontal space is limited. The compact widget is taller than normal to accommodate the smaller width. + +:::note +Widget size only applies to Managed and Non-Interactive modes. Invisible widgets have no visual footprint regardless of size configuration. +::: + + + + ```html title="Normal size (default)" wrap +
+ ``` + ```html title="Flexible size" wrap +
+ ``` + ```html title="Compact size" wrap +
+ ``` +
+ + ```js title="Normal size (default)" wrap + turnstile.render('#widget-container', { + sitekey: '' + }); + ``` + ```js title="Flexible size" wrap + turnstile.render('#widget-container', { + sitekey: '', + size: 'flexible' + }); + ``` + ```js title="Compact size" wrap + turnstile.render('#widget-container', { + sitekey: '', + size: 'compact' + }); + ``` + +
+ +--- + +## Theme options + +Customize the widget's visual appearance to match your website's design. + +- `auto` (default): Automatically matches the visitor's system theme preference. Auto is recommended for most implementations as it respects the visitor's preferences and provides the best accessibility experience. +- `light`: Light theme with bright colors and clear contrast. Light theme works best on bright backgrounds and provides high contrast for readability. +- `dark`: Dark theme optimized for dark interfaces. Dark theme is ideal for dark interfaces, gaming sites, or applications with dark color schemes. + + + + ```html title="Auto theme (default)" wrap +
+ ``` + ```html title="Light theme" wrap +
+ ``` + ```html title="Dark theme" wrap +
+ ``` +
+ + ```js title="Auto theme (default)" wrap + turnstile.render('#widget-container', { + sitekey: '' + }); + ``` + ```js title="Light theme" wrap + turnstile.render('#widget-container', { + sitekey: '', + theme: 'light' + }); + ``` + ```js title="Dark theme" wrap + turnstile.render('#widget-container', { + sitekey: '', + theme: 'dark' + }); + ``` + +
+ +--- + +## Appearance modes + +Control when the widget becomes visible to visitors using the appearance mode. + +- `always` (default): The widget is always visible from page load. This is the best option for most implementations where you want your visitors to see the widget immediately as it provides clear visual feedback that security verification is in place. +- `execute`: The widget only becomes visible after the challenge begins. This is useful for when you need to control the timing of widget appearance, such as showing it only when a visitor starts filling out a form or selecting a submit button. +- `interaction-only`: The widget becomes visible only when visitor interaction is required and provides the cleanest visitor experience. Most visitors will never see the widget, but suspected bots will encounter the interactive challenge. + +:::note +Appearance modes only affect visible widget types (Managed and Non-Interactive). Invisible widgets are never shown regardless of the appearance setting. +::: + + + + ```html title="Always visible (default)" wrap +
+ ``` + ```html title="Visible only after challenge begins" wrap +
+ ``` + ```html title="Visible only when interaction is needed" wrap +
+ ``` +
+ + ```js title="Always visible (default)" wrap + turnstile.render('#widget-container', { + sitekey: '' + }); + ``` + ```js title="Visible only after challenge begins" wrap + turnstile.render('#widget-container', { + sitekey: '', + appearance: 'execute' + }); + ``` + ```js title="Visible only when interaction is needed" wrap + turnstile.render('#widget-container', { + sitekey: '', + appearance: 'interaction-only' + }); + ``` + +
+ +--- + +## Execution modes + +Control when the challenge runs and a token is generated. + +- `render` (default): The challenge runs automatically after calling the `render()` function and provides immediate protection as soon as the widget loads. The challenge runs in the background while the page loads, ensuring the token is ready when the visitor submits data. +- `execute`: The challenge runs after calling the `turnstile.execute()` function separately and gives you precise control over when verification occurs. This option is useful for multi-step forms, conditional verification, or when you want to defer the challenge until the visitor actually attempts to submit data. This can improve page load performance and visitor experience by only running verification when needed. + + **Common scenarios** + - Multi-step forms: Run verification only on the final step. + - Conditional protection: Only verify visitors who meet certain criteria. + - Performance optimization: Defer verification to reduce initial page load time. + - User-triggered verification: Let visitors manually start the verification process. + + + + ```html title="Auto execution (default)" wrap +
+ ``` + ```html title="Manual execution" wrap +
+ ``` +
+ + ```js title="Auto execution (default)" wrap + turnstile.render('#widget-container', { + sitekey: '' + }); + ``` + ```js title="Manual execution" wrap + turnstile.render('#widget-container', { + sitekey: '', + execution: 'execute' + }); + ``` + ```js title="Execute the challenge later" wrap + turnstile.execute('#widget-container'); + ``` + +
+ +--- + +## Language configuration + +Set the language for the widget interface. + +- `auto` (default): Uses the visitor's browser language preference. +- Specific language codes: ISO 639-1 two-letter codes, such as `es`, `fr`, `de`. +- Language and region: Combined codes for regional variants, such as `en-US`, `es-MX`, `pt-BR`. + +:::note[Notes] +- When set to `auto`, Turnstile automatically detects the visitor's preferred language from their browser settings. +- If a requested language is not supported, Turnstile falls back to English. +- Language affects all visitor-facing text including loading messages, error states, and accessibility labels. +- Setting specific languages can improve visitor experience for international audiences. +::: + + + + ```html title="Auto language (default)" wrap +
+ ``` + ```html title="Specific language" wrap +
+ ``` + ```html title="Language and country" wrap +
+ ``` +
+ + ```js title="Auto language (default)" wrap + turnstile.render('#widget-container', { + sitekey: '' + }); + ``` + ```js title="Specific language" wrap + turnstile.render('#widget-container', { + sitekey: '', + language: 'es' + }); + ``` + +
+ +--- + +## Callback configuration + +Handle widget events with callbacks. + +- `callback`: Triggered when the challenge is successfully completed. +- `error-callback`: Triggered when an error occurs during the challenge. +- `expired-callback`: Triggered when a token expires (before timeout). +- `timeout-callback`: Triggered when an interactive challenge times out. + +The success callback receives a token that must be validated on your server using the Siteverify API. Tokens are single-use and expire after 300 seconds (five minutes). + + + + ```html wrap +
+ + ``` +
+ + ```js wrap + turnstile.render('#widget-container', { + sitekey: '', + callback: function(token) { + console.log('Challenge Success:', token); + }, + 'error-callback': function(errorCode) { + console.log('Challenge Error:', errorCode); + }, + 'expired-callback': function() { + console.log('Token expired'); + }, + 'timeout-callback': function() { + console.log('Challenge timed out'); + } + }); + ``` + +
+ +### Best practices + +- Always implement the success callback to handle the token and proceed with form submission or next steps. +- Use error callbacks for graceful error handling and visitor feedback. +- Monitor expired tokens to refresh challenges before they become invalid. +- Handle timeouts to guide visitors through challenge resolution. + +--- + +## Advanced configuration options + +### Retry behavior + +Control how Turnstile handles failed challenges. + +- `auto` (default): Automatically retries failed challenges. Auto retry provides better visitor experience by automatically recovering from temporary network issues or processing errors. +- `never`: Disables automatic retry. This requires manual intervention and gives you full control over error handling in applications that need custom retry logic. +- `retry-interval`: Controls the time between retry attempts (default: 8000ms) and lets you balance between quick recovery and server load. + +```html title="Auto retry (default)" wrap +
+``` + +```html title="Disable retry" wrap +
+``` + +```html title="Custom retry interval (8000ms default)" +
+``` + +### Refresh behavior + +Control how Turnstile handles token expiration and interactive timeouts. + +- `refresh-expired`: Controls behavior when tokens expire (`auto`, `manual`, `never`). +- `refresh-timeout`: Controls behavior when interactive challenges timeout (`auto`, `manual`, `never`). + +#### Benefits + +- `auto` refresh provides seamless visitor experience but uses more resources. +- `manual` refresh gives visitors control but requires them to take action. +- `never` refresh requires your application to handle all refresh logic. + +Different strategies can be used for token expiration versus interactive timeouts based on your visitor experience requirements. + +```html title="Auto refresh expired tokens (default)" wrap +
+``` + +```html title="Manual refresh" wrap +
+ +``` + +```html title="Auto refresh timeouts (default for Managed mode)" +
+``` + +### Custom data + +Add custom identifiers and data to your challenges. + +- `action`: A custom identifier for analytics and differentiation (maximum 32 characters). +- `cData`: Custom payload data returned during validation (maximum 255 characters). + +#### Use cases + +- Action tracking: Differentiate between login, signup, contact forms, and more. in your analytics. +- Visitor context: Pass visitor IDs, session information, or other contextual data. +- A/B testing: Track different widget configurations or page variants. +- Fraud detection: Include additional context for risk assessment. + +:::caution +Both action and cData fields only accept alphanumeric characters, underscores (_), and hyphens (-). +::: + +```html title="Add custom action identifier" wrap +
+``` + +```html title="Add custom data payload" wrap +
+``` + +### Form integration + +Configure how Turnstile integrates with HTML forms. + +When enabled, Turnstile automatically creates a hidden `` element with the verification token. This gets submitted along with your other form data, making server-side validation straightforward. + +- `response-field`: Determines whether to create a hidden form field with the token (`default: true`) +- `response-field-name`: Custom name for the hidden form field (`default: cf-turnstile-response`) + +#### Benefits + +- Automatic form integration means that the token is included when the form is submitted, requiring no additional JavaScript. +- Custom field names helps avoid conflicts with existing form fields. +- Disabled response fields give you full control over token handling for complex form scenarios. + +```html title="Custom response field name" wrap +
+``` + +```html title="Disable response field" wrap +
+``` + +--- + +## Complete configuration reference + +| JavaScript Render Parameters | Data Attribute | Description | +| ----------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sitekey` | `data-sitekey` | Every widget has a sitekey. This sitekey is associated with the corresponding widget configuration and is created upon the widget creation. | +| `action` | `data-action` | A customer value that can be used to differentiate widgets under the same sitekey in analytics and which is returned upon validation. This can only contain up to 32 alphanumeric characters including `_` and `-`. | +| `cData` | `data-cdata` | A customer payload that can be used to attach customer data to the challenge throughout its issuance and which is returned upon validation. This can only contain up to 255 alphanumeric characters including `_` and `-`. | +| `callback` | `data-callback` | A JavaScript callback invoked upon success of the challenge. The callback is passed a token that can be validated. | +| `error-callback` | `data-error-callback` | A JavaScript callback invoked when there is an error (e.g. network error or the challenge failed). Refer to [Client-side errors](/turnstile/troubleshooting/client-side-errors/). | +| `execution` | `data-execution` | Execution controls when to obtain the token of the widget and can be on `render` (default) or on `execute`. Refer to [Execution Modes](/turnstile/get-started/client-side-rendering/#execution-modes) for more information. | +| `expired-callback` | `data-expired-callback` | A JavaScript callback invoked when the token expires and does not reset the widget. | +| `before-interactive-callback` | `data-before-interactive-callback` | A JavaScript callback invoked before the challenge enters interactive mode. | +| `after-interactive-callback` | `data-after-interactive-callback` | A JavaScript callback invoked when challenge has left interactive mode. | +| `unsupported-callback` | `data-unsupported-callback` | A JavaScript callback invoked when a given client/browser is not supported by Turnstile. | +| `theme` | `data-theme` | The widget theme. Can take the following values: `light`, `dark`, `auto`.

The default is `auto`, which respects the visitor preference. This can be forced to light or dark by setting the theme accordingly. | +| `language` | `data-language` | Language to display, must be either: `auto` (default) to use the language that the visitor has chosen, or an ISO 639-1 two-letter language code (e.g. `en`) or language and country code (e.g. `en-US`). Refer to the [list of supported languages](/turnstile/reference/supported-languages/) for more information. | +| `tabindex` | `data-tabindex` | The tabindex of Turnstile's iframe for accessibility purposes. The default value is `0`. | +| `timeout-callback` | `data-timeout-callback` | A JavaScript callback invoked when the challenge presents an interactive challenge but was not solved within a given time. A callback will reset the widget to allow a visitor to solve the challenge again. | +| `response-field` | `data-response-field` | A boolean that controls if an input element with the response token is created, defaults to `true`. | +| `response-field-name` | `data-response-field-name` | Name of the input element, defaults to `cf-turnstile-response`. | +| `size` | `data-size` | The widget size. Can take the following values: `normal`, `flexible`, `compact`. | +| `retry` |  `data-retry` | Controls whether the widget should automatically retry to obtain a token if it did not succeed. The default is `auto`, which will retry automatically. This can be set to `never` to disable retry on failure. | +| `retry-interval` |  `data-retry-interval` | When `retry` is set to `auto`, `retry-interval` controls the time between retry attempts in milliseconds. Value must be a positive integer less than `900000`, defaults to `8000`. | +| `refresh-expired` |  `data-refresh-expired` | Automatically refreshes the token when it expires. Can take `auto`, `manual`, or `never`, defaults to `auto`. | +| `refresh-timeout` | `data-refresh-timeout` | Controls whether the widget should automatically refresh upon entering an interactive challenge and observing a timeout. Can take `auto` (automatically refreshes upon encountering an interactive timeout), `manual` (prompts the visitor to manually refresh) or `never` (will show a timeout), defaults to `auto`. Only applies to widgets of Managed mode. | +| `appearance` | `data-appearance` | Appearance controls when the widget is visible. It can be `always` (default), `execute`, or `interaction-only`. Refer to [Appearance modes](/turnstile/get-started/client-side-rendering/#appearance-modes) for more information. | +| `feedback-enabled` | `data-feedback-enabled` | Allows Cloudflare to gather visitor feedback upon widget failure. It can be `true` (default) or `false`. | + +### Examples + +```html title="Responsive design widget" wrap +
+
data-size="flexible" data-theme="auto">
+
+ +``` + +```html title="Mobile-optimized compact widget" wrap +
data-size="compact" data-theme="light" data-language="en"> +
+``` \ No newline at end of file diff --git a/src/content/docs/turnstile/get-started/index.mdx b/src/content/docs/turnstile/get-started/index.mdx index d1fd7b94a0a9905..844c11d617410c2 100644 --- a/src/content/docs/turnstile/get-started/index.mdx +++ b/src/content/docs/turnstile/get-started/index.mdx @@ -1,77 +1,117 @@ --- -title: Get started with Turnstile +title: Get started pcx_content_type: get-started sidebar: order: 4 - group: - label: Get started - --- -import { GlossaryTooltip, Render } from "~/components" -This guide will get you started on setting up the Turnstile widget. +import { Render, LinkButton } from "~/components" + +This guide will get you started on setting up the Turnstile widget to protect your website from bots while maintaining a seamless user experience. -If you are currently using a CAPTCHA service, you can copy and paste our script wherever you have deployed the existing script today. +## Prerequisites -## Get a sitekey and secret key +Before you begin, you must have: -To start using the Turnstile widget, you will need to obtain a sitekey and a secret key. The sitekey and secret key are always associated with one widget and cannot be reused for other widgets. +- A Cloudflare account +- A website or web application to protect +- Basic knowledge of HTML and your preferred server-side language + +--- -The sitekey is public and used to invoke the Turnstile widget on your site. +## Process -The sitekey and secret key are generated upon the creation of a widget, allowing communication between your site and Cloudflare to verify responses for a solved challenge from Turnstile. Make sure you keep the secret key safe for security reasons. +Turnstile widgets are the foundation of your bot protection implementation. -:::note + -You can find special sitekeys to be used for testing in the [testing](/turnstile/troubleshooting/testing/) section. +:::note[Important] +Regardless of how you create and manage your widgets, you will still need to [embed the widget](/turnstile/get-started/client-side-rendering/) on your webpage and [validate the token](/turnstile/get-started/server-side-validation/) on your server. ::: -### New sites +Implementing Turnstile involves two essential components that work together: -1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/turnstile) and select your account. -2. Go to **Turnstile**. -3. Select **Add widget** and fill out the site name and [your website's hostname or select from your existing websites](/turnstile/concepts/hostname-management/) on Cloudflare. -4. Select the widget mode. -5. (Optional) Opt in for [pre-clearance support](/turnstile/concepts/pre-clearance-support/). -6. Copy your sitekey and secret key. +1. Client-side: [Embed the widget](/turnstile/get-started/client-side-rendering/) -### Existing sites + Add the Turnstile widget to your webpage to challenge visitors and generate verification tokens. -1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/turnstile) and select your account. -2. Go to **Turnstile**. -3. In the widget overview, select **Settings**. -4. Confirm the [hostnames](/turnstile/concepts/hostname-management/) configured. -5. (Optional) Opt in for [pre-clearance support](/turnstile/concepts/pre-clearance-support/). -6. Copy your sitekey and secret key. +2. Server-side: [Validate the token](/turnstile/get-started/server-side-validation/) -## Add the Turnstile widget to your site + Verify the tokens on your server using the Siteverify API to ensure they are authentic and have not been tampered with. -To add the Turnstile widget: +Refer to [Implementation](#implementation) below for guidance on how to implement Turnstile on your website. -1. Insert the Turnstile script snippet in your HTML's `` element: +--- -
+## Implementation -```html - -``` +Follow the steps below to implement Turnstile. -
+### 1. Create your widget -2. [Render the client-side integration](/turnstile/get-started/client-side-rendering/) +First, you must create a Turnstile widget to get your sitekey and secret key. - - [Explicit rendering](/turnstile/get-started/client-side-rendering/#explicitly-render-the-turnstile-widget) - - [Implicit rendering](/turnstile/get-started/client-side-rendering/#implicitly-render-the-turnstile-widget) +Select your preferred implementation method: -## Validate the server-side response + - +### 2. Embed the widget -After you have installed the Turnstile widget on your site, you must configure your server to validate the Turnstile response. -Refer to [Server-side validation](/turnstile/get-started/server-side-validation/). +Add the Turnstile widget to your webpage forms and applications. -:::note +Refer to [Embed the widget](/turnstile/get-started/client-side-rendering/) to learn more about implicit and explicit rendering methods. -Rendering the client-side integration & validating the server-side response are both necessary to allow Turnstile to function properly. +:::note[Testing] + ::: + +### 3. Validate tokens + +Implement server-side validation to verify the tokens generated by your widgets. + +Refer to [Validate the token](/turnstile/get-started/server-side-validation/) to secure your implementation with proper token verification. + + +:::note[Testing] + +::: + + +## Additional implementation options + +### Mobile configuration + +Special considerations are necessary for mobile applications and WebView implementations. + +Refer to [Mobile implementation](/turnstile/get-started/mobile-implementation/) for more information on mobile application integration. + +### Migration from other CAPTCHAs + +If you are currently using reCAPTCHA, hCaptcha, or another CAPTCHA service, Turnstile can be a drop-in replacement. You can copy and paste our script wherever you have deployed the existing script today. + +Refer to [Migration](/turnstile/migration/) for step-by-step migration guidance from other CAPTCHA services. + +--- + +## Security requirements + + + +--- + +## Best practices + +### Security + +- Protect your secret keys. Never expose secret keys in client-side code. +- Rotate your keys regularly. Use API or dashboard to rotate secret keys periodically. +- Restrict your hostnames. Only allow widgets on domains that you control. +- Monitor the usage. Use analytics to detect unusual patterns. + +### Operational + +- Use descriptive names. Name widgets based on their purpose, such as "Login Form" or "Contact Page". +- Separate your environments. Use different widgets for development, staging, and production. +- Keep track of which widgets are used at which locations. +- Store your widget configurations in version control when using Terraform. diff --git a/src/content/docs/turnstile/get-started/mobile-implementation.mdx b/src/content/docs/turnstile/get-started/mobile-implementation.mdx index 9da942a50d7633c..723f702a0451575 100644 --- a/src/content/docs/turnstile/get-started/mobile-implementation.mdx +++ b/src/content/docs/turnstile/get-started/mobile-implementation.mdx @@ -40,7 +40,7 @@ When implementing Turnstile with WebViews, the user agent must stay consistent a ## Use clearance cookies -When using [clearance cookies](/turnstile/concepts/pre-clearance-support/) with Turnstile, make sure that it is executed in the same environment where the challenges will occur, including the same browser and device configuration. The `cf_clearance` cookie will be only accepted in the same configured domain for Turnstile widget with the corresponding zone. Domains configured with the Turnstile widget must match the Cloudflare zone that issues [challenges](/cloudflare-challenges/). +When using [clearance cookies](/cloudflare-challenges/concepts/clearance/#pre-clearance-support-in-turnstile) with Turnstile, make sure that it is executed in the same environment where the challenges will occur, including the same browser and device configuration. The `cf_clearance` cookie will be only accepted in the same configured domain for Turnstile widget with the corresponding zone. Domains configured with the Turnstile widget must match the Cloudflare zone that issues [challenges](/cloudflare-challenges/). If pre-clearance is done in a different environment, the clearance cookie may become invalid and lead to more issued challenges. diff --git a/src/content/docs/turnstile/get-started/pre-clearance.mdx b/src/content/docs/turnstile/get-started/pre-clearance.mdx deleted file mode 100644 index 261bd2d40b16013..000000000000000 --- a/src/content/docs/turnstile/get-started/pre-clearance.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: Enable pre-clearance cookies -pcx_content_type: get-started -sidebar: - order: 3 - label: Pre-clearance cookies - ---- - -import { Render } from "~/components" - -## Prerequisites - -To enable pre-clearance, you must ensure that the hostname of the Turnstile widget matches the zone with the WAF rules. During the Turnstile configuration setup in the Cloudflare dashboard, you can see the registered zones. Select the appropriate hostname from this list. - -The prerequisite is crucial for pre-clearance to function properly. If set up correctly, visitors who successfully solve Turnstile will receive a cookie with the security clearance level set by the customer. When encountering a WAF challenge on the same zone, they will bypass additional challenges for the configured clearance level and below. - -For more details on managing hostnames, refer to the [Hostname Management documentation](/turnstile/concepts/hostname-management/). - - - -## Enable pre-clearance on a new site - -1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/login) and select your account. -2. Go to **Turnstile** > **Add widget**. -3. Under **Would you like to opt for pre-clearance for this site?** select **Yes**. -4. Choose the pre-clearance level from the select box. -5. Select **Create**. - -## Enable pre-clearance on an existing site - -1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/login) and select your account. -2. Go to **Turnstile**. -3. Go to the existing widget or site and select **Settings**. -4. Under **Would you like to opt for pre-clearance for this site?** select **Yes**. -5. Choose the pre-clearance level from the select box. -6. Select **Update**. \ No newline at end of file diff --git a/src/content/docs/turnstile/get-started/server-side-validation.mdx b/src/content/docs/turnstile/get-started/server-side-validation.mdx index 34082e3f3837cae..e46240c2d565c80 100644 --- a/src/content/docs/turnstile/get-started/server-side-validation.mdx +++ b/src/content/docs/turnstile/get-started/server-side-validation.mdx @@ -1,302 +1,674 @@ --- -title: Server-side validation +title: Validate the token pcx_content_type: get-started sidebar: - order: 2 + order: 4 --- -import { GlossaryTooltip, Render, TabItem, Tabs } from "~/components"; +import { Render, TabItem, Tabs } from "~/components"; - +Learn how to securely validate Turnstile tokens on your server using the Siteverify API. -Turnstile needs to be verified using Siteverify because it is a front-end widget that creates a token which is cryptographically secured. To ensure that a token is not forged by an attacker or has not been consumed yet, it is necessary to check the validity of a token using Cloudflare's Siteverify API. +:::caution[Mandatory server-side validation] -You must call the Siteverify endpoint to validate the Turnstile widget response from your website's backend. The widget response must only be considered valid once it has been verified by the Siteverify endpoint. The presence of a response alone is not enough to verify it as it does not protect from replay or forgery attacks. In some cases, Turnstile may purposely create invalid responses that are rejected by the Siteverify API. +You must call Siteverify API to complete your Turnstile configuration. The client-side widget alone does not provide protection. -Tokens issued to Turnstile using the success callbacks, via explicit or implicit rendering, must be validated using the Siteverify endpoint. The Siteverify API will only validate a token once. If a token has already been checked, the Siteverify API will yield an error on subsequent verification attempts indicating that a token has already been consumed. +You must validate tokens on your server because tokens can be forged by attackers, expire after 5 minutes (300 seconds), and are single-use and cannot be validated twice. -:::note +Client-side verification alone leaves major security vulnerabilities. +::: -A Turnstile token can have up to 2048 characters. +## Process -It is also valid for 300 seconds before it is rejected by Siteverify. -::: +1. Client generates token: Visitor completes Turnstile challenge on your webpage. +2. Token sent to server: Form submission includes the Turnstile token. +3. Server validates token: Your server calls Cloudflare's Siteverify API. +4. Cloudflare responds: Returns `success` or `failure` and additional data. +5. Server takes action: Allow or reject the original request based on validation. -The Siteverify endpoint needs to be passed a secret key that is associated with the sitekey. The secret key will be provisioned alongside the sitekey when you create a widget. Furthermore, the response needs to be passed to the Siteverify endpoint. +## Siteverify API overview -A response may only be validated once. If the same response is presented twice, the second and each subsequent request will generate an error stating that the response has already been consumed. If an application requires to retry failed requests, it must utilize the idempotency functionality. You can do so by providing a UUID as the `idempotency_key` parameter of your `POST` request when initially validating the response and the same UUID with any subsequent request for that response. +```shell title="Endpoint" +POST https://challenges.cloudflare.com/turnstile/v0/siteverify +``` -:::note +### Request format -Refer to the [full demo on GitHub](https://github.com/cloudflare/turnstile-demo-workers/blob/main/src/index.mjs). -::: +The API accepts both `application/x-www-form-urlencoded` and `application/json` requests, but always returns JSON responses. -
+#### Required parameters -```sh title="Example using cURL" -curl 'https://challenges.cloudflare.com/turnstile/v0/siteverify' --data 'secret=verysecret&response=' -``` +| Parameter | Required | Description | +| --- | --- | --- | +| `secret` | Yes | Your widget's secret key from the Cloudflare dashboard | +| `response` | Yes | The token from the client-side widget | +| `remoteip` | No | The visitor's IP address | +| `idempotency_key` | No | UUID for retry protection | -```sh output -{ - "success": true, - "error-codes": [], - "challenge_ts": "2022-10-06T00:07:23.274Z", - "hostname": "example.com" +#### Token characteristics + +- Maximum length: 2048 characters +- Validity period: 300 seconds (5 minutes) from generation +- Single use: Each token can only be validated once +- Automatic expiry: Tokens automatically expire and cannot be reused + +--- + +## Basic validation examples + + + +```js + const SECRET_KEY = 'your-secret-key'; + + async function validateTurnstile(token, remoteip) { + const formData = new FormData(); + formData.append('secret', SECRET_KEY); + formData.append('response', token); + formData.append('remoteip', remoteip); + + try { + const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { + method: 'POST', + body: formData + }); + + const result = await response.json(); + return result; + } catch (error) { + console.error('Turnstile validation error:', error); + return { success: false, 'error-codes': ['internal-error'] }; + } + } + + // Usage in form handler + async function handleFormSubmission(request) { + const body = await request.formData(); + const token = body.get('cf-turnstile-response'); + const ip = request.headers.get('CF-Connecting-IP') || + request.headers.get('X-Forwarded-For') || + 'unknown'; + + const validation = await validateTurnstile(token, ip); + + if (validation.success) { + // Token is valid - process the form + console.log('Valid submission from:', validation.hostname); + return processForm(body); + } else { + // Token is invalid - reject the submission + console.log('Invalid token:', validation['error-codes']); + return new Response('Invalid verification', { status: 400 }); + } + } +``` + + +```json +const SECRET_KEY = 'your-secret-key'; + +async function validateTurnstile(token, remoteip) { + try { + const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + secret: SECRET_KEY, + response: token, + remoteip: remoteip + }) + }); + + const result = await response.json(); + return result; + } catch (error) { + console.error('Turnstile validation error:', error); + return { success: false, 'error-codes': ['internal-error'] }; + } } ``` + + +```php + $secret, + 'response' => $token + ]; + + if ($remoteip) { + $data['remoteip'] = $remoteip; + } + + $options = [ + 'http' => [ + 'header' => "Content-type: application/x-www-form-urlencoded\r\n", + 'method' => 'POST', + 'content' => http_build_query($data) + ] + ]; + + $context = stream_context_create($options); + $response = file_get_contents($url, false, $context); + + if ($response === FALSE) { + return ['success' => false, 'error-codes' => ['internal-error']]; + } + + return json_decode($response, true); +} -
- - - -```js title="Example using fetch from Cloudflare Workers" -// This is the demo secret key. In production, we recommend -// you store your secret key(s) safely. -const SECRET_KEY = "1x0000000000000000000000000000000AA"; - -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 endpoint. - let formData = new FormData(); - formData.append("secret", SECRET_KEY); - formData.append("response", token); - formData.append("remoteip", ip); - - const url = "https://challenges.cloudflare.com/turnstile/v0/siteverify"; - const result = await fetch(url, { - body: formData, - method: "POST", - }); - - const outcome = await result.json(); - if (outcome.success) { - // ... - } +// Usage +$secret_key = 'your-secret-key'; +$token = $_POST['cf-turnstile-response'] ?? ''; +$remoteip = $_SERVER['HTTP_CF_CONNECTING_IP'] ?? + $_SERVER['HTTP_X_FORWARDED_FOR'] ?? + $_SERVER['REMOTE_ADDR']; + +$validation = validateTurnstile($token, $secret_key, $remoteip); + +if ($validation['success']) { + // Valid token - process form + echo "Form submission successful!"; + // Process your form data here +} else { + // Invalid token - show error + echo "Verification failed. Please try again."; + error_log('Turnstile validation failed: ' . implode(', ', $validation['error-codes'])); } +?> ``` - + +```python +import requests + +def validate_turnstile(token, secret, remoteip=None): + url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify' + + data = { + 'secret': secret, + 'response': token + } + + if remoteip: + data['remoteip'] = remoteip + + try: + response = requests.post(url, data=data, timeout=10) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + print(f"Turnstile validation error: {e}") + return {'success': False, 'error-codes': ['internal-error']} + +# Usage with Flask +from flask import Flask, request, jsonify + +app = Flask(__name__) +SECRET_KEY = 'your-secret-key' + +@app.route('/submit-form', methods=['POST']) +def submit_form(): + token = request.form.get('cf-turnstile-response') + remoteip = request.headers.get('CF-Connecting-IP') or \ + request.headers.get('X-Forwarded-For') or \ + request.remote_addr + + validation = validate_turnstile(token, SECRET_KEY, remoteip) + + if validation['success']: + # Valid token - process form + return jsonify({'status': 'success', 'message': 'Form submitted successfully'}) + else: + # Invalid token - reject submission + return jsonify({ + 'status': 'error', + 'message': 'Verification failed', + 'errors': validation['error-codes'] + }), 400 +``` + + +```java +import org.springframework.web.client.RestTemplate; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; + +@Service +public class TurnstileService { + private static final String SITEVERIFY_URL = "https://challenges.cloudflare.com/turnstile/v0/siteverify"; + private final String secretKey = "your-secret-key"; + private final RestTemplate restTemplate = new RestTemplate(); + + public TurnstileResponse validateToken(String token, String remoteip) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("secret", secretKey); + params.add("response", token); + if (remoteip != null) { + params.add("remoteip", remoteip); + } + + HttpEntity> request = new HttpEntity<>(params, headers); + + try { + ResponseEntity response = restTemplate.postForEntity( + SITEVERIFY_URL, request, TurnstileResponse.class); + return response.getBody(); + } catch (Exception e) { + TurnstileResponse errorResponse = new TurnstileResponse(); + errorResponse.setSuccess(false); + errorResponse.setErrorCodes(List.of("internal-error")); + return errorResponse; + } + } +} - - -```js title="Example using fetch from Cloudflare Workers" -// This is the demo secret key. In production, we recommend -// you store your secret key(s) safely. -const SECRET_KEY = '1x0000000000000000000000000000000AA'; -​ -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 endpoint. - const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'; - const result = await fetch(url, { - body: JSON.stringify({ - secret: SECRET_KEY, - response: token, - remoteip: ip - }), - method: 'POST', - headers: { - 'Content-Type': 'application/json' - } - }); -​ - const outcome = await result.json(); - if (outcome.success) { - // ... - } +// Controller usage +@PostMapping("/submit-form") +public ResponseEntity submitForm( + @RequestParam("cf-turnstile-response") String token, + HttpServletRequest request) { + + String remoteip = request.getHeader("CF-Connecting-IP"); + if (remoteip == null) { + remoteip = request.getHeader("X-Forwarded-For"); + } + if (remoteip == null) { + remoteip = request.getRemoteAddr(); + } + + TurnstileResponse validation = turnstileService.validateToken(token, remoteip); + + if (validation.isSuccess()) { + // Valid token - process form + return ResponseEntity.ok("Form submitted successfully"); + } else { + // Invalid token - reject submission + return ResponseEntity.badRequest() + .body("Verification failed: " + validation.getErrorCodes()); + } } ``` + + +```csharp +using System.Text.Json; - - - - -```js title="Example using idempotency functionality" -// This is the demo secret key. In production, we recommend -// you store your secret key(s) safely. -const SECRET_KEY = "1x0000000000000000000000000000000AA"; - -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 endpoint. - let formData = new FormData(); - formData.append("secret", SECRET_KEY); - formData.append("response", token); - formData.append("remoteip", ip); - const idempotencyKey = crypto.randomUUID(); - formData.append("idempotency_key", idempotencyKey); - - const url = "https://challenges.cloudflare.com/turnstile/v0/siteverify"; - const firstResult = await fetch(url, { - body: formData, - method: "POST", - }); - const firstOutcome = await firstResult.json(); - if (firstOutcome.success) { - // ... - } - - // A subsequent validation request to the "/siteverify" - // API endpoint for the same token as before, providing - // the associated idempotency key as well. - const subsequentResult = await fetch(url, { - body: formData, - method: "POST", - }); - - const subsequentOutcome = await subsequentResult.json(); - if (subsequentOutcome.success) { - // ... - } +public class TurnstileService +{ + private readonly HttpClient _httpClient; + private readonly string _secretKey = "your-secret-key"; + private const string SiteverifyUrl = "https://challenges.cloudflare.com/turnstile/v0/siteverify"; + + public TurnstileService(HttpClient httpClient) + { + _httpClient = httpClient; + } + + public async Task ValidateTokenAsync(string token, string remoteip = null) + { + var parameters = new Dictionary + { + { "secret", _secretKey }, + { "response", token } + }; + + if (!string.IsNullOrEmpty(remoteip)) + { + parameters.Add("remoteip", remoteip); + } + + var postContent = new FormUrlEncodedContent(parameters); + + try + { + var response = await _httpClient.PostAsync(SiteverifyUrl, postContent); + var stringContent = await response.Content.ReadAsStringAsync(); + + return JsonSerializer.Deserialize(stringContent); + } + catch (Exception ex) + { + return new TurnstileResponse + { + Success = false, + ErrorCodes = new[] { "internal-error" } + }; + } + } } -``` +// Controller usage +[HttpPost("submit-form")] +public async Task SubmitForm([FromForm] string cfTurnstileResponse) +{ + var remoteip = HttpContext.Request.Headers["CF-Connecting-IP"].FirstOrDefault() ?? + HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault() ?? + HttpContext.Connection.RemoteIpAddress?.ToString(); + + var validation = await _turnstileService.ValidateTokenAsync(cfTurnstileResponse, remoteip); + + if (validation.Success) + { + // Valid token - process form + return Ok("Form submitted successfully"); + } + else + { + // Invalid token - reject submission + return BadRequest($"Verification failed: {string.Join(", ", validation.ErrorCodes)}"); + } +} +``` + + +--- - - -```js title="Example using idempotency functionality" -// This is the demo secret key. In production, we recommend -// you store your secret key(s) safely. -const SECRET_KEY = '1x0000000000000000000000000000000AA'; -​ -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 endpoint. - const idempotencyKey = crypto.randomUUID(); - const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'; - const firstResult = await fetch(url, { - body: JSON.stringify({ - secret: SECRET_KEY, - response: token, - remoteip: ip, - idempotency_key: idempotencyKey - }), - method: 'POST', - headers: { - 'Content-Type': 'application/json' - } - }); -​ - const firstOutcome = await firstResult.json(); - if (firstOutcome.success) { - // ... - } -​ - // A subsequent validation request to the "/siteverify" - // API endpoint for the same token as before, providing - // the associated idempotency key as well. - const subsequentResult = await fetch(url, { - body: JSON.stringify({ - secret: SECRET_KEY, - response: token, - remoteip: ip, - idempotency_key: idempotencyKey - }), - method: 'POST', - headers: { - 'Content-Type': 'application/json' - } - }); -​ - const subsequentOutcome = await subsequentResult.json(); - if (subsequentOutcome.success) { - // ... - } -​ +## Advanced validation techniques + +```js title="Idempotency keys for retry operation" +const crypto = require('crypto'); + +async function validateWithRetry(token, remoteip, maxRetries = 3) { + const idempotencyKey = crypto.randomUUID(); + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + const formData = new FormData(); + formData.append('secret', SECRET_KEY); + formData.append('response', token); + formData.append('remoteip', remoteip); + formData.append('idempotency_key', idempotencyKey); + + const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { + method: 'POST', + body: formData + }); + + const result = await response.json(); + + if (response.ok) { + return result; + } + + // If this is the last attempt, return the error + if (attempt === maxRetries) { + return result; + } + + // Wait before retrying (exponential backoff) + await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000)); + + } catch (error) { + if (attempt === maxRetries) { + return { success: false, 'error-codes': ['internal-error'] }; + } + } + } } ``` - +```js title="Enhanced validation with custom checks" +async function validateTurnstileEnhanced(token, remoteip, expectedAction = null, expectedHostname = null) { + const validation = await validateTurnstile(token, remoteip); + + if (!validation.success) { + return { + valid: false, + reason: 'turnstile_failed', + errors: validation['error-codes'] + }; + } + + // Check if action matches expected value (if specified) + if (expectedAction && validation.action !== expectedAction) { + return { + valid: false, + reason: 'action_mismatch', + expected: expectedAction, + received: validation.action + }; + } + + // Check if hostname matches expected value (if specified) + if (expectedHostname && validation.hostname !== expectedHostname) { + return { + valid: false, + reason: 'hostname_mismatch', + expected: expectedHostname, + received: validation.hostname + }; + } + + // Check token age (warn if older than 4 minutes) + const challengeTime = new Date(validation.challenge_ts); + const now = new Date(); + const ageMinutes = (now - challengeTime) / (1000 * 60); + + if (ageMinutes > 4) { + console.warn(`Token is ${ageMinutes.toFixed(1)} minutes old`); + } + + return { + valid: true, + data: validation, + tokenAge: ageMinutes + }; +} -## Accepted parameters +// Usage +const result = await validateTurnstileEnhanced( + token, + remoteip, + 'login', // expected action + 'example.com' // expected hostname +); + +if (result.valid) { + // Process the request + console.log('Validation successful:', result.data); +} else { + // Handle validation failure + console.log('Validation failed:', result.reason); +} +``` -| POST Parameter | Required/Optional | Description | -| ----------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------- | -| `secret` | Required | The widget's secret key. The secret key can be found under widget settings in the Cloudflare dashboard under Turnstile. | -| `response` | Required | The response provided by the Turnstile client-side render on your site. | -| `remoteip` | Optional | The visitor's IP address. | -| `idempotency_key` | Optional | The UUID to be associated with the response. | +--- -:::note +## API response format -The `remoteip` parameter helps to prevent abuse by ensuring the current visitor is the one who received the token. This is currently not strictly validated. -::: + + +```json title="Example" +{ + "success": true, + "challenge_ts": "2022-02-28T15:14:30.096Z", + "hostname": "example.com", + "error-codes": [], + "action": "login", + "cdata": "sessionid-123456789", + "metadata": { + "ephemeral_id": "x:9f78e0ed210960d7693b167e" + } +} +``` + + +```json title="Example" +{ + "success": false, + "error-codes": ["invalid-input-response"] +} +``` + + + +### Response fields + +| Field | Description | +| --- | --- | +| `success` | Boolean indicating if validation was successful | +| `challenge_ts` | ISO timestamp when the challenge was solved | +| `hostname` | Hostname where the challenge was served | +| `error-codes` | Array of error codes (if validation failed) | +| `action` | Custom action identifier from client-side | +| `cdata` | Custom data payload from client-side | +| `metadata.ephemeral_id` | Device fingerprint ID (Enterprise only) | + +### Error codes reference + +| Error code | Description | Action required | +| --- | --- | --- | +| `missing-input-secret` | Secret parameter not provided | Ensure secret key is included | +| `invalid-input-secret` | Secret key is invalid or expired | Check your secret key in the Cloudflare dashboard | +| `missing-input-response` | Response parameter was not provided | Ensure token is included | +| `invalid-input-response` | Token is invalid, malformed, or expired | User should retry the challenge | +| `bad-request` | Request is malformed | Check request format and parameters | +| `timeout-or-duplicate` | Token has already been validated | Each token can only be used once | +| `internal-error` | Internal error occurred | Retry the request | -The Siteverify endpoint behaves similar to reCAPTCHA's or hCaptcha's Siteverify endpoint. -The API accepts `application/x-www-form-urlencoded` and `application/json` requests, but the response type will always be `application/json`. +--- -It always contains a `success` property, either true or false, indicating whether the operation was successful or not. +## Implementation + +``` js title="Example implementation" +class TurnstileValidator { + constructor(secretKey, timeout = 10000) { + this.secretKey = secretKey; + this.timeout = timeout; + } + + async validate(token, remoteip, options = {}) { + // Input validation + if (!token || typeof token !== 'string') { + return { success: false, error: 'Invalid token format' }; + } + + if (token.length > 2048) { + return { success: false, error: 'Token too long' }; + } + + // Prepare request + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), this.timeout); + + try { + const formData = new FormData(); + formData.append('secret', this.secretKey); + formData.append('response', token); + + if (remoteip) { + formData.append('remoteip', remoteip); + } + + if (options.idempotencyKey) { + formData.append('idempotency_key', options.idempotencyKey); + } + + const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { + method: 'POST', + body: formData, + signal: controller.signal + }); + + const result = await response.json(); + + // Additional validation + if (result.success) { + if (options.expectedAction && result.action !== options.expectedAction) { + return { + success: false, + error: 'Action mismatch', + expected: options.expectedAction, + received: result.action + }; + } + + if (options.expectedHostname && result.hostname !== options.expectedHostname) { + return { + success: false, + error: 'Hostname mismatch', + expected: options.expectedHostname, + received: result.hostname + }; + } + } + + return result; + + } catch (error) { + if (error.name === 'AbortError') { + return { success: false, error: 'Validation timeout' }; + } + + console.error('Turnstile validation error:', error); + return { success: false, error: 'Internal error' }; + + } finally { + clearTimeout(timeoutId); + } + } +} -
+// Usage +const validator = new TurnstileValidator(process.env.TURNSTILE_SECRET_KEY); -```json title="Successful validation response" {2} -{ - "success": true, - "challenge_ts": "2022-02-28T15:14:30.096Z", - "hostname": "example.com", - "error-codes": [], - "action": "login", - "cdata": "sessionid-123456789", - "metadata":{ - "ephemeral_id": "x:9f78e0ed210960d7693b167e" - } +const result = await validator.validate(token, remoteip, { + expectedAction: 'login', + expectedHostname: 'example.com' +}); + +if (result.success) { + // Process the request +} else { + // Handle failure + console.log('Validation failed:', result.error); } + ``` -
+--- -- `challenge_ts` is the ISO timestamp for the time the challenge was solved. -- `hostname` is the hostname for which the challenge was served. -- `action` is the customer widget identifier passed to the widget on the client side. This is used to differentiate widgets using the same sitekey in analytics. Its integrity is protected by modifications from an attacker. It is recommended to validate that the action matches an expected value. -- `cdata` is the customer data passed to the widget on the client side. This can be used by the customer to convey state. It is integrity protected by modifications from an attacker. -- `error-codes` is a list of errors that occurred. -- (Enterprise only) `ephemeral_id` returns the [Ephemeral ID](/turnstile/concepts/ephemeral-id/) in Siteverify. +## Testing -In case of a validation failure, the response should be similar to the following: + -
+--- -```json title="Failed validation response" {2} -{ - "success": false, - "error-codes": ["invalid-input-response"] -} -``` +## Best practices + +### Security + +- Store your secret keys securely. Use environment variables or secure key management. +- Validate the token on every request. Never trust client-side validation alone. +- Check additional fields. Validate the action and hostname when specified. +- Monitor for abuse and log failed validations and unusual patterns. +- Use HTTPS. Always validate over secure connections. -
+### Performance -A validation error is indicated by having the `success` property set to `false`. A list of error codes is provided to indicate why a response has failed to verify. The response may also contain additional fields based on whether Turnstile Siteverify was able to parse the response successfully or unsuccessfully. +- Set reasonable timeouts. Do not wait indefinitely for Siteverify responses. +- Implement retry logic and handle temporary network issues. +- Cache validation results for the same token, if it is needed for your flow. +- Monitor your API latency. Track the Siteverify response time. -## Error codes +### Error handling -|
Error code
| Description | -| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `missing-input-secret` | The secret parameter was not passed. | -| `invalid-input-secret` | The secret parameter was invalid, did not exist, or is a [testing secret key](/turnstile/troubleshooting/testing) with a non-testing response. | -| `missing-input-response` | The response parameter (token) was not passed. | -| `invalid-input-response` | The response parameter (token) is invalid or has expired. Most of the time, this means a fake token has been used. If the error persists, contact customer support. | -| `bad-request` | The request was rejected because it was malformed. | -| `timeout-or-duplicate` | The response parameter (token) has already been validated before. This means that the token was issued five minutes ago and is no longer valid, or it was already redeemed. | -| `internal-error` | An internal error happened while validating the response. The request can be retried. | \ No newline at end of file +- Have fallback behavior for API failures. +- Use user-friendly messaging. Do not expose internal error details to users. +- Properly log errors for debugging without exposing secrets. +- Rate limit to protect against validation flooding. diff --git a/src/content/docs/turnstile/get-started/terraform.mdx b/src/content/docs/turnstile/get-started/terraform.mdx deleted file mode 100644 index a0077458d31a990..000000000000000 --- a/src/content/docs/turnstile/get-started/terraform.mdx +++ /dev/null @@ -1,263 +0,0 @@ ---- -pcx_content_type: how-to -title: Terraform -sidebar: - order: 4 ---- - -:::note[Requirements] - -This guide assumes that you have the [Terraform](https://developer.hashicorp.com/terraform/tutorials/certification-associate-tutorials/install-cli) command installed on your machine. -::: - -[Terraform](https://developer.hashicorp.com/terraform/tutorials/certification-associate-tutorials/install-cli) is a tool for building, changing, and versioning infrastructure, and provides components and documentation for building [Cloudflare resources](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs). Listed below are examples to help you get started with Turnstile using Terraform. For a more generalized guide on configuring Cloudflare and Terraform, visit our [Getting Started with Terraform and Cloudflare](https://blog.cloudflare.com/getting-started-with-terraform-and-cloudflare-part-1/) blog post. - -## Create a Turnstile widget with Terraform - -### Create an API token - -Create an [API Token](/fundamentals/api/get-started/create-token/) with the **Account > Turnstile > Edit** permission. Next, you need to export this secret in our environment variables: - -```sh title="Export your token" -export CLOUDFLARE_API_TOKEN= -``` - -### Create a Turnstile widget - -See the example configuration below when creating a Turnstile widget. - -```tf title="cloudflare.tf" {15-32} -terraform { - required_providers { - cloudflare = { - source = "cloudflare/cloudflare" - version = "~> 4" - } - } -} - -variable "account_id" { - description = "Your Cloudflare Account ID." - # eg: 6be2041a37d48aaaa9c686434f1709f0 -} - -resource "cloudflare_turnstile_widget" "example" { - account_id = var.account_id - name = "My Terraform-managed widget" - domains = ["example.com"] - mode = "managed" -} - -output "turnstile_example_sitekey" { - description = "Sitekey" - value = cloudflare_turnstile_widget.example.id - # Note: the `id` is your sitekey. -} - -output "turnstile_example_secretkey" { - description = "Secret key" - value = cloudflare_turnstile_widget.example.secret - sensitive = true -} - -``` - -:::note - -The `id` field in the `cloudflare_turnstile_widget.example` resource is your Turnstile widget's sitekey. -::: - -### Initialize Terraform and the Cloudflare provider - -Run the command `terraform init` to set up your Terraform working directory, enabling it to interact with Cloudflare services. This process involves downloading the required provider plugins, establishing backend storage for your state files, and creating a local `.terraform` directory to store configuration data. - -```sh title="Initialize command" -terraform init -``` - -```sh output -Initializing the backend... - -Initializing provider plugins... -- Reusing previous version of cloudflare/cloudflare from the dependency lock file -- Installing cloudflare/cloudflare v4.5.0... -- Installed cloudflare/cloudflare v4.5.0 (self-signed, key ID C76001609EE3B136) - -Partner and community providers are signed by their developers. -If you'd like to know more about provider signing, you can read about it here: -https://www.terraform.io/docs/cli/plugins/signing.html - -Terraform has been successfully initialized! - -You may now begin working with Terraform. Try running "terraform plan" to see -any changes that are required for your infrastructure. All Terraform commands -should now work. - -If you ever set or change modules or backend configuration for Terraform, -rerun this command to reinitialize your working directory. If you forget, other -commands will detect it and remind you to do so if necessary. -``` - -### Review the Terraform plan - -You can run `terraform plan`, which will output any proposed changes. This will prompt you for your Cloudflare Account ID. Make sure to review the plan carefully. - -```sh title="Review command" -terraform plan -``` - -```sh output - -var.account_id - Your Cloudflare Account ID. - - Enter a value: 6be2041a37d48aaaa9c686434f1709f0 - -Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: - + create - -Terraform will perform the following actions: - - # cloudflare_turnstile_widget.example will be created - + resource "cloudflare_turnstile_widget" "example" { - + account_id = "6be2041a37d48aaaa9c686434f1709f0" - + domains = [ - + "example.com", - ] - + id = (known after apply) - + mode = "managed" - + name = "My Terraform-managed widget" - + secret = (sensitive value) - } - -Plan: 1 to add, 0 to change, 0 to destroy. - -Changes to Outputs: - + turnstile_example_secretkey = (sensitive value) - + turnstile_example_sitekey = (known after apply) - -─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── - -Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. -``` - -### Apply the Terraform changes - -Once the changes look accurate and you are comfortable moving forward, apply them using the `terraform apply` command. - -```sh title="Apply command" -terraform apply --auto-approve -``` - -```sh output - -var.account_id - Your Cloudflare Account ID. - - Enter a value: 6be2041a37d48aaaa9c686434f1709f0 - -Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: - + create - -Terraform will perform the following actions: - - # cloudflare_turnstile_widget.example will be created - + resource "cloudflare_turnstile_widget" "example" { - + account_id = "6be2041a37d48aaaa9c686434f1709f0" - + domains = [ - + "example.com", - ] - + id = (known after apply) - + mode = "managed" - + name = "My Terraform-managed widget" - + secret = (sensitive value) - } - -Plan: 1 to add, 0 to change, 0 to destroy. - -Changes to Outputs: - + turnstile_example_secretkey = (sensitive value) - + turnstile_example_sitekey = (known after apply) -cloudflare_turnstile_widget.example: Creating... -cloudflare_turnstile_widget.example: Creation complete after 1s [id=0x4AAAAAAAEe4wQdBshJxBeK] - -Apply complete! Resources: 1 added, 0 changed, 0 destroyed. - -Outputs: - -turnstile_example_secretkey = -turnstile_example_sitekey = "0x4AAAAAAAEe4wQdBshJxBeK" -``` - -You have successfully created a Turnstile widget. Go to the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/turnstile) to view its configuration and analytics in a user-friendly interface. - -### Retrieve the secret key - -Use `terraform output` to get your secret key. - -```sh title="Secret key" -terraform output turnstile_example_secretkey -``` - -```sh output -"0x4AAAAAAAEe4xWueFq9yX8ypjlimbk1Db4" -``` - -:::note - -For advanced usage, refer to our [Terraform resource documentation](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/turnstile_widget). -::: - -## Import a Turnstile widget - -### Prerequisites - -Before you can import the Turnstile widget in Terraform, you must: - -- [Install the `cf-terraforming` tool](/terraform/advanced-topics/import-cloudflare-resources/#cf-terraforming). -- [Create a Cloudflare API token](/fundamentals/api/get-started/create-token/). -- [Initialize Terraform and the Cloudflare provider](#initialize-terraform-and-the-cloudflare-provider). - -### Import Turnstile widgets in Terraform - -Run the `cf-terraforming generate` command and add the output below to your `.tf` file. - -```sh title="Generate command" -cf-terraforming generate --resource-type cloudflare_turnstile_widget --account 6be2041a37d48aaaa9c686434f1709f0 -``` - -```sh output -resource "cloudflare_turnstile_widget" "terraform_managed_resource_0x4AAAAAAAEk5sP3rwf91fe8" { - account_id = "6be2041a37d48aaaa9c686434f1709f0" - domains = ["example.net"] - mode = "managed" - name = "test site" - region = "world" -} - -resource "cloudflare_turnstile_widget" "terraform_managed_resource_0x4AAAAAAAE0wwg0H1StXlOx" { - account_id = "6be2041a37d48aaaa9c686434f1709f0" - domains = ["example.com"] - mode = "managed" - name = "My Terraform-managed widget" -} - -resource "cloudflare_turnstile_widget" "terraform_managed_resource_0x4AAAAAAAF1z4LbxEka5UBh" { - account_id = "6be2041a37d48aaaa9c686434f1709f0" - domains = ["example.org"] - mode = "managed" - name = "My website" -} -``` - -Run the `cf-terraforming import` command and the resulting commands below. - -```sh title="Import command" -cf-terraforming import --resource-type cloudflare_turnstile_widget --account 6be2041a37d48aaaa9c686434f1709f0 -``` - -```sh output -terraform import cloudflare_turnstile_widget.terraform_managed_resource_0x4AAAAAAAEg5sP3rwf91fe8 6be2041a37d48aaaa9c686434f1709f0/0x4AAAAAAAEk5sP3rwf91fe8 -terraform import cloudflare_turnstile_widget.terraform_managed_resource_0x4AAAAAAAE0gwg0H1StXlOx 6be2041a37d48aaaa9c686434f1709f0/0x4AAAAAAAE0wwg0H1StXlOx -terraform import cloudflare_turnstile_widget.terraform_managed_resource_0x4AAAAAAAE2z4LbxEka5UBh 6be2041a37d48aaaa9c686434f1709f0/0x4AAAAAAAF1z4LbxEka5UBh -``` diff --git a/src/content/docs/turnstile/get-started/widget-management/api.mdx b/src/content/docs/turnstile/get-started/widget-management/api.mdx new file mode 100644 index 000000000000000..b88d127faba0356 --- /dev/null +++ b/src/content/docs/turnstile/get-started/widget-management/api.mdx @@ -0,0 +1,65 @@ +--- +title: API +pcx_content_type: how-to +sidebar: + order: 2 +--- + +import { APIRequest } from "~/components" + +Use the [Cloudflare API](/api/resources/turnstile/) for programmatic widget management and automation. + +## Prerequisites + +Before you begin, you must have: + +- A Cloudflare API token with `Account:Turnstile:Edit` permissions +- An account ID found in your Cloudflare dashboard + +### Create a widget via the API + + + +### Manage widgets via the API + + + + + + + + + + \ No newline at end of file diff --git a/src/content/docs/turnstile/get-started/widget-management/dashboard.mdx b/src/content/docs/turnstile/get-started/widget-management/dashboard.mdx new file mode 100644 index 000000000000000..fd02939b6160c04 --- /dev/null +++ b/src/content/docs/turnstile/get-started/widget-management/dashboard.mdx @@ -0,0 +1,48 @@ +--- +title: Cloudflare dashboard +pcx_content_type: how-to +sidebar: + order: 1 +--- + +import { Steps } from "~/components"; + + +The Cloudflare dashboard provides a user-friendly interface for creating and managing widgets. + +## Create a widget + + + 1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and select your account. + 2. Go to **Turnstile**. + 3. Select **Add widget**. + 4. Fill out the required information: + - **Widget name**: A descriptive name for your widget. + - **Hostname management**: Domains where the widget will be used. + - **Widget mode**: Choose from Managed, Non-Interactive, or Invisible. + 5. (Optional) Configure **Pre-clearance support** for single-page applications. + 6. Select **Create** to save your widget. + 7. Copy your sitekey and secret key, and store the secret key securely. + + +## Manage existing widgets + +### View widget details + + + 1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and select your account. + 2. Go to **Turnstile**. + 3. Select any existing widget to view its configuration. + 4. Access analytics, settings, and performance metrics. + + +### Update widget configuration + + + 1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and select your account. + 2. Go to **Turnstile**. + 3. Select any existing widget to view its configuration. + 4. Select **Settings**. + 5. Modify hostnames, mode, or other settings. + 6. Select **Save** to apply changes. + \ No newline at end of file diff --git a/src/content/docs/turnstile/get-started/widget-management/index.mdx b/src/content/docs/turnstile/get-started/widget-management/index.mdx new file mode 100644 index 000000000000000..cb33d06d6913b8b --- /dev/null +++ b/src/content/docs/turnstile/get-started/widget-management/index.mdx @@ -0,0 +1,15 @@ +--- +title: Widget management +pcx_content_type: get-started +sidebar: + order: 1 + label: Overview + group: + hideIndex: true +--- + +import { DirectoryListing } from "~/components" + +Refer to the following pages for more information about managing Turnstile widgets: + + \ No newline at end of file diff --git a/src/content/docs/turnstile/get-started/widget-management/terraform.mdx b/src/content/docs/turnstile/get-started/widget-management/terraform.mdx new file mode 100644 index 000000000000000..5fce491be240085 --- /dev/null +++ b/src/content/docs/turnstile/get-started/widget-management/terraform.mdx @@ -0,0 +1,192 @@ +--- +title: Terraform +pcx_content_type: how-to +sidebar: + order: 3 +--- + +import { Render } from "~/components" + +Manage Turnstile widgets as code using Terraform for version control and automated deployments. + +## Prerequisites + +Before you begin, you must have: + +- [Terraform](https://terraform.io/) installed +- A Cloudflare API token with `Account:Turnstile:Edit permissions` +- (Optional) A `cf-terraforming` tool for importing existing widgets + +## Setup + +### 1. Configure provider + +Create a `main.tf` file. + + + +```tf +terraform { + required_providers { + cloudflare = { + source = "cloudflare/cloudflare" + version = "~> 4.0" + } + } +} + +provider "cloudflare" { + api_token = var.cloudflare_api_token +} + +variable "cloudflare_api_token" { + description = "Cloudflare API Token" + type = string + sensitive = true +} + +variable "account_id" { + description = "Cloudflare Account ID" + type = string +} +``` + +### 2. Define widgets + +```tf +resource "cloudflare_turnstile_widget" "login_form" { + account_id = var.account_id + name = "Login Form Widget" + domains = ["example.com", "www.example.com"] + mode = "managed" + region = "world" +} + +resource "cloudflare_turnstile_widget" "api_protection" { + account_id = var.account_id + name = "API Protection" + domains = ["api.example.com"] + mode = "invisible" + region = "world" +} + +# Output the sitekeys for use in your application +output "login_sitekey" { + value = cloudflare_turnstile_widget.login_form.sitekey +} + +output "api_sitekey" { + value = cloudflare_turnstile_widget.api_protection.sitekey +} +``` + +### 3. Environment variables + +Create a `.env` file or set environment variables. + +```shell +export TF_VAR_cloudflare_api_token="your-api-token" +export TF_VAR_account_id="your-account-id" +``` + +--- + +## Terraform commands + +### Initialize and plan + +```shell title="Initialize Terraform" +terraform init +``` + +```shell title="Plan changes" +terraform plan +``` + +```shell title="Apply configuration" +terraform apply +``` + +### Manage changes + +```shell title="Update widget configuration" +terraform plan +``` + +```shell title="Apply changes" +terraform apply +``` + +```shell title="Destroy widgets" +terraform destroy +``` + +--- + +## Advanced Terraform configuration + +### Multiple environments + +```tf +locals { + environments = { + dev = { + domains = ["dev.example.com"] + mode = "managed" + } + staging = { + domains = ["staging.example.com"] + mode = "non_interactive" + } + prod = { + domains = ["example.com", "www.example.com"] + mode = "invisible" + } + } +} + +resource "cloudflare_turnstile_widget" "app_widget" { + for_each = local.environments + + account_id = var.account_id + name = "App Widget - ${each.key}" + domains = each.value.domains + mode = each.value.mode + region = "world" +} +``` + +### Widget with Enterprise features + +```tf +resource "cloudflare_turnstile_widget" "enterprise_widget" { + account_id = var.account_id + name = "Enterprise Form" + domains = ["enterprise.example.com"] + mode = "managed" + region = "world" + offlabel = true # Remove Cloudflare branding + bot_fight_mode = true # Enable bot fight mode +} +``` + +--- + +## Import existing widgets + +Use [`cf-terraforming`](/terraform/advanced-topics/import-cloudflare-resources/#cf-terraforming) to import existing widgets. + +```shell title="Install cf-terraforming" +go install github.com/cloudflare/cf-terraforming/cmd/cf-terraforming@latest +``` + +```shell title="Generate Terraform configuration from existing widgets" +cf-terraforming generate \ + --resource-type cloudflare_turnstile_widget \ + --account $ACCOUNT_ID +``` + +```shell title="Import existing widget" +terraform import cloudflare_turnstile_widget.existing_widget \ + $ACCOUNT_ID/$WIDGET_SITEKEY +``` \ No newline at end of file diff --git a/src/content/docs/turnstile/glossary.mdx b/src/content/docs/turnstile/glossary.mdx deleted file mode 100644 index 5bedb6bdaa1c8d6..000000000000000 --- a/src/content/docs/turnstile/glossary.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: Glossary -pcx_content_type: glossary -sidebar: - order: 14 - ---- - -import { Glossary } from "~/components" - -Review the definitions for terms used across Cloudflare's Turnstile documentation. - - diff --git a/src/content/docs/turnstile/index.mdx b/src/content/docs/turnstile/index.mdx index f07e579cb0fcfa0..63b8476c11edf6a 100644 --- a/src/content/docs/turnstile/index.mdx +++ b/src/content/docs/turnstile/index.mdx @@ -16,6 +16,8 @@ import { Plan, RelatedProduct, Render, + CardGrid, + LinkTitleCard } from "~/components"; @@ -30,17 +32,23 @@ Turnstile can be embedded into any website without sending traffic through Cloud -Rather than try to unilaterally deprecate and replace CAPTCHA with a single alternative, we built a platform to test many alternatives and rotate new challenges in and out as they become more or less effective. +## How Turnstile works -With Turnstile, we adapt the actual challenge outcome to the individual visitor or browser. First, we run a series of small non-interactive JavaScript challenges gathering more signals about the visitor/browser environment. Those challenges include, proof-of-work, proof-of-space, probing for web APIs, and various other challenges for detecting browser-quirks and human behavior. As a result, we can fine-tune the difficulty of the challenge to the specific request and avoid ever showing a visual puzzle to a user. +Turnstile adapts the challenge outcome to the individual visitor or browser. First, we run a series of small non-interactive JavaScript challenges to gather signals about the visitor or browser environment. -Turnstile also includes machine learning models that detect common features of end visitors who were able to pass a challenge before. The computational hardness of those initial challenges may vary by visitor, but is targeted to run fast. +These challenges include proof-of-work, proof-of-space, probing for web APIs, and various other challenges for detecting browser-quirks and human behavior. As a result, we can fine-tune the difficulty of the challenge to the specific request and avoid showing a visual or interactive puzzle to a user. + +:::note +For detailed information on Turnstile's data privacy practices, refer to the [Turnstile Privacy Addendum](https://www.cloudflare.com/turnstile-privacy-policy/). +::: + +### Widget types Turnstile [widget types](/turnstile/concepts/widget/) include: -- A non-interactive challenge. -- A non-intrusive interactive challenge (such as checking a box), if the visitor is a suspected bot. -- An invisible challenge to the browser. +- **Non-interactive**: Visitors never need to interact with the widget. +- **Managed**: Visitors are presented with an interactive checkbox if they are a suspected bot. +- **Invisible**: The widget is completely hidden from the visitor. --- @@ -50,36 +58,6 @@ Turnstile is WCAG 2.1 AA compliant. --- -## Privacy policy - -For information on Turnstile's data privacy policy, refer to the [Turnstile Privacy Addendum](https://www.cloudflare.com/turnstile-privacy-policy/). - ---- - -## Availability - - - -Refer to [Cloudflare Turnstile's product page](https://www.cloudflare.com/products/turnstile/) for more information on Turnstile's plans. - ---- - -## Get started - -{" "} - - Get started - - Migration guides - - Dashboard - - ---- - ## Features @@ -88,8 +66,8 @@ Refer to [Cloudflare Turnstile's product page](https://www.cloudflare.com/produc Integrate Cloudflare challenges on single-page applications (SPAs) by allowing Turnstile to issue a Pre-Clearance cookie. @@ -104,7 +82,22 @@ Refer to [Cloudflare Turnstile's product page](https://www.cloudflare.com/produc your domain from bad bots. + +Detect and mitigate Distributed Denial of Service (DDoS) attacks using Cloudflare's Autonomous Edge. + + Get automatic protection from vulnerabilities and the flexibility to create custom rules. + +--- + +## More resources + + + + +Learn more about Turnstile's plan availability. + + diff --git a/src/content/docs/turnstile/migration/index.mdx b/src/content/docs/turnstile/migration/index.mdx index 5e7d3a34537ca96..16c33708ca8d251 100644 --- a/src/content/docs/turnstile/migration/index.mdx +++ b/src/content/docs/turnstile/migration/index.mdx @@ -2,7 +2,7 @@ title: Migration pcx_content_type: migration sidebar: - order: 2 + order: 6 --- If you are using alternative CAPTCHA services, you can switch to Cloudflare Turnstile using the guides below to assist with the upgrade process. diff --git a/src/content/docs/turnstile/plans.mdx b/src/content/docs/turnstile/plans.mdx new file mode 100644 index 000000000000000..4b10f3e2238b669 --- /dev/null +++ b/src/content/docs/turnstile/plans.mdx @@ -0,0 +1,42 @@ +--- +title: Plans +type: overview +hideChildren: true +pcx_content_type: navigation +sidebar: + order: 2 +head: + - tag: title + content: Cloudflare Turnstile plans + +--- + +import { FeatureTable, Tabs, TabItem, Details } from "~/components" + +Cloudflare Turnstile is available on the following plans: + +
+The Free plan is best for: + +- Personal websites and blogs +- Small to medium businesses +- Development and testing environments +- Most production applications +
+ +
+The Enterprise plan is best for: +- Large enterprises with high-volume traffic +- Organizations requiring advanced bot detection and device fingerprinting +- Organizations requiring custom branding, companies with strict compliance requirements +- Multi-domain or complex hosting environments +
+ + + +:::note[Notes] + +- If you upgrade from Free to Enterprise, your existing widgets and configurations will be preserved during the upgrade process. +- Free users are limited to 20 widgets per account. Customers with Enterprise Bot Management and Enterprise Turnstile can have this limit increased. Contact your account team to discuss your requirements. +- Turnstile can be used independently without requiring other Cloudflare services. +::: \ No newline at end of file diff --git a/src/content/docs/turnstile/reference/content-security-policy.mdx b/src/content/docs/turnstile/reference/content-security-policy.mdx index bca2ac36d45dc10..512d4122d7de42a 100644 --- a/src/content/docs/turnstile/reference/content-security-policy.mdx +++ b/src/content/docs/turnstile/reference/content-security-policy.mdx @@ -24,6 +24,6 @@ You cannot set your own CSP and/or Referer-Policy via meta tags or [Transform ru ## Pre-clearance support -If you are using [Turnstile in pre-clearance mode](/turnstile/concepts/pre-clearance-support/), Turnstile sets the `cf_clearance` cookie by doing a fetch request to a special endpoint in [`/cdn-cgi/`](/fundamentals/reference/cdn-cgi-endpoint/) of your domain. +If you are using [Turnstile in pre-clearance mode](/cloudflare-challenges/concepts/clearance/#pre-clearance-support-in-turnstile), Turnstile sets the `cf_clearance` cookie by doing a fetch request to a special endpoint in [`/cdn-cgi/`](/fundamentals/reference/cdn-cgi-endpoint/) of your domain. For this request to succeed, your `connect-src` directive must include `'self'`. diff --git a/src/content/docs/turnstile/reference/index.mdx b/src/content/docs/turnstile/reference/index.mdx index b693738154da028..d10f4d43b428dc4 100644 --- a/src/content/docs/turnstile/reference/index.mdx +++ b/src/content/docs/turnstile/reference/index.mdx @@ -2,7 +2,7 @@ title: Reference pcx_content_type: navigation sidebar: - order: 9 + order: 10 group: hideIndex: true diff --git a/src/content/docs/turnstile/get-started/supported-browsers.mdx b/src/content/docs/turnstile/reference/supported-browsers.mdx similarity index 92% rename from src/content/docs/turnstile/get-started/supported-browsers.mdx rename to src/content/docs/turnstile/reference/supported-browsers.mdx index c3409059494964a..f18fb51e7040f20 100644 --- a/src/content/docs/turnstile/get-started/supported-browsers.mdx +++ b/src/content/docs/turnstile/reference/supported-browsers.mdx @@ -3,6 +3,6 @@ pcx_content_type: concept title: Supported browsers external_link: /cloudflare-challenges/reference/supported-browsers/ sidebar: - order: 5 + order: 3 --- diff --git a/src/content/docs/turnstile/reference/workers-templates/index.mdx b/src/content/docs/turnstile/reference/workers-templates/index.mdx index 18b14719507fafd..ec09a82c96524a3 100644 --- a/src/content/docs/turnstile/reference/workers-templates/index.mdx +++ b/src/content/docs/turnstile/reference/workers-templates/index.mdx @@ -3,7 +3,7 @@ type: overview pcx_content_type: navigation title: Workers templates sidebar: - order: 3 + order: 4 group: hideIndex: true --- diff --git a/src/content/docs/turnstile/reference/workers-templates/price-scraping.mdx b/src/content/docs/turnstile/reference/workers-templates/price-scraping.mdx index 210aa4a7a603aa7..6538f77373d8c21 100644 --- a/src/content/docs/turnstile/reference/workers-templates/price-scraping.mdx +++ b/src/content/docs/turnstile/reference/workers-templates/price-scraping.mdx @@ -11,7 +11,7 @@ pcx_content_type: example title: Price scraping demo: https://pricing.httpmachine.com/ sidebar: - order: 4 + order: 1 description: You can use the templates below for a demo application in Workers where product details are shown, but pricing information is not revealed until the Turnstile widget is solved. diff --git a/src/content/docs/turnstile/troubleshooting/index.mdx b/src/content/docs/turnstile/troubleshooting/index.mdx index 888958ad3f5a449..c3e5b4edbece13c 100644 --- a/src/content/docs/turnstile/troubleshooting/index.mdx +++ b/src/content/docs/turnstile/troubleshooting/index.mdx @@ -2,7 +2,7 @@ title: Troubleshooting pcx_content_type: navigation sidebar: - order: 7 + order: 8 group: hideIndex: true diff --git a/src/content/docs/turnstile/troubleshooting/testing.mdx b/src/content/docs/turnstile/troubleshooting/testing.mdx index 535c90511051169..97bb7fe6c43576b 100644 --- a/src/content/docs/turnstile/troubleshooting/testing.mdx +++ b/src/content/docs/turnstile/troubleshooting/testing.mdx @@ -12,7 +12,7 @@ import { GlossaryTooltip } from "~/components" The following sitekeys and secret keys are available for testing. It is recommended that you use these keys in your development environment to ensure the challenges running in Turnstile do not conflict with your developer tools. -To test locally with real keys, you need to add your testing hostnames (like `localhost`) to your [domain allowlist](/turnstile/concepts/hostname-management/). +To test locally with real keys, you need to add your testing hostnames (like `localhost`) to your [domain allowlist](/turnstile/additional-configuration/hostname-management/). Dummy sitekeys can be used from any domain, including on `localhost`. diff --git a/src/content/docs/turnstile/turnstile-analytics/challenge-outcomes.mdx b/src/content/docs/turnstile/turnstile-analytics/challenge-outcomes.mdx index 7d43aea617ada4c..ccbac2e34274d01 100644 --- a/src/content/docs/turnstile/turnstile-analytics/challenge-outcomes.mdx +++ b/src/content/docs/turnstile/turnstile-analytics/challenge-outcomes.mdx @@ -33,7 +33,7 @@ By analyzing these metrics, you can identify trends such as high failure rates i Turnstile's solve rate is a critical metric that helps gauge how many legitimate visitors are passing a challenge. Solve rates can be broken down into the total number of challenges solved and whether they are interactive, non-interactive, or pre-clearance solves. -If you are using [managed mode](/turnstile/concepts/widget/#widget-types), you can monitor how many of your visitors were prompted interactivity to check the box on the widget (interactive solves) and how many were verified without any disruptions to their experience (non-interactive solves). +If you are using [managed mode](/turnstile/concepts/widget/#managed-mode-recommended), you can monitor how many of your visitors were prompted interactivity to check the box on the widget (interactive solves) and how many were verified without any disruptions to their experience (non-interactive solves). For example, the solve rate values in your analytics may look like this: @@ -43,6 +43,6 @@ For example, the solve rate values in your analytics may look like this: - **Non-interactive solves**: Challenges solved without requiring the visitor to click a checkbox. - **Interactive solves**: Challenges solved that required visitor interaction to be solved. -- [**Pre-clearance solves**](/turnstile/concepts/pre-clearance-support/): Challenges solved that issued the `cf_clearance` cookie along with the Turnstile token. +- [**Pre-clearance solves**](/cloudflare-challenges/concepts/clearance/#pre-clearance-support-in-turnstile): Challenges solved that issued the `cf_clearance` cookie along with the Turnstile token. A low solve rate might indicate increased bot activity attempting to bypass Turnstile or anomalous traffic patterns that require further investigation. \ No newline at end of file diff --git a/src/content/docs/turnstile/turnstile-analytics/index.mdx b/src/content/docs/turnstile/turnstile-analytics/index.mdx index 28c9bd60627a2b2..d7a1ede6dfd267c 100644 --- a/src/content/docs/turnstile/turnstile-analytics/index.mdx +++ b/src/content/docs/turnstile/turnstile-analytics/index.mdx @@ -2,7 +2,7 @@ title: Turnstile Analytics pcx_content_type: how-to sidebar: - order: 6 + order: 5 description: Use Turnstile Analytics to view the number of challenges issued, the challenge solve rate, and the metrics of issued challenges. diff --git a/src/content/docs/turnstile/tutorials/implicit-vs-explicit-rendering.mdx b/src/content/docs/turnstile/tutorials/implicit-vs-explicit-rendering.mdx index 81468371ba198b0..03c24cd4e8fbcc8 100644 --- a/src/content/docs/turnstile/tutorials/implicit-vs-explicit-rendering.mdx +++ b/src/content/docs/turnstile/tutorials/implicit-vs-explicit-rendering.mdx @@ -50,7 +50,7 @@ Once a challenge has been solved, a token is passed to the success callback. Thi

-
+

@@ -74,7 +74,7 @@ An invisible input with the name `cf-turnstile-response` is added and will be se

-
+

@@ -105,7 +105,7 @@ To set up explicit rendering: ```html function onloadTurnstileCallback() { turnstile.render('#my-turnstile', { - sitekey: '', + sitekey: '<>', callback: function(token) { console.log(`Challenge Success ${token}`); }, @@ -136,7 +136,7 @@ function onloadTurnstileCallback() { ``` -Replace `YOUR_SITE_KEY` with your actual Turnstile site key. +Replace `` with your actual Turnstile site key. ## Handle the login request diff --git a/src/content/docs/turnstile/tutorials/login-pages.mdx b/src/content/docs/turnstile/tutorials/login-pages.mdx index 48a0c6c69774b50..fe85aa513ff6361 100644 --- a/src/content/docs/turnstile/tutorials/login-pages.mdx +++ b/src/content/docs/turnstile/tutorials/login-pages.mdx @@ -29,7 +29,7 @@ This tutorial will guide you through integrating Cloudflare Turnstile to protect ## Add the Turnstile widget to your HTML form 1. Add the Turnstile widget to your form. -2. Replace `YOUR-SITE-KEY` with the sitekey from Cloudflare. +2. Replace `` with the sitekey from Cloudflare. 3. Add a `data-callback` attribute to the Turnstile widget div. This JavaScript function will be called when the challenge is successful. 4. Ensure your submit button is initially disabled. @@ -53,7 +53,7 @@ This tutorial will guide you through integrating Cloudflare Turnstile to protect -
+
diff --git a/src/content/docs/waiting-room/reference/configuration-settings.mdx b/src/content/docs/waiting-room/reference/configuration-settings.mdx index 405eeafb3ed97a4..fa92d5acd981919 100644 --- a/src/content/docs/waiting-room/reference/configuration-settings.mdx +++ b/src/content/docs/waiting-room/reference/configuration-settings.mdx @@ -209,7 +209,7 @@ You can customize a variety of options for your waiting rooms. Yes, defaults invisible. - The type of Turnstile widget to use - refer to the [Turnstile documentation](/turnstile/concepts/widget/#widget-types) for details. Valid values are off, invisible, visible_non_interactive, and visible_managed. Setting this to off will completely disable the Turnstile integration. + The type of Turnstile widget to use - refer to the [Turnstile documentation](/turnstile/concepts/widget/#widget-modes) for details. Valid values are off, invisible, visible_non_interactive, and visible_managed. Setting this to off will completely disable the Turnstile integration. Setting this to invisible makes sense for most rooms, unless you would like end users to be aware the challenge is running. diff --git a/src/content/partials/turnstile/challenge-behavior.mdx b/src/content/partials/turnstile/challenge-behavior.mdx index 29bd8ff2927ea40..f60a738f5071a3e 100644 --- a/src/content/partials/turnstile/challenge-behavior.mdx +++ b/src/content/partials/turnstile/challenge-behavior.mdx @@ -5,4 +5,4 @@ Cloudflare issues challenges through the [Challenge Platform](/cloudflare-challenges/), which is the same underlying technology powering [Turnstile](/turnstile/). -In contrast to our Challenge page offerings, Turnstile allows you to run challenges anywhere on your site in a less-intrusive way without requiring the use of Cloudflare’s CDN. +In contrast to our Challenge page offerings, Turnstile allows you to run challenges anywhere on your site in a less-intrusive way without requiring the use of Cloudflare's CDN. diff --git a/src/content/partials/turnstile/implementation-methods.mdx b/src/content/partials/turnstile/implementation-methods.mdx new file mode 100644 index 000000000000000..fea01d4e19100e0 --- /dev/null +++ b/src/content/partials/turnstile/implementation-methods.mdx @@ -0,0 +1,12 @@ +--- +{} + +--- + +import { LinkButton } from "~/components" + +Cloudflare dashboard + +API + +Terraform \ No newline at end of file diff --git a/src/content/partials/turnstile/security-requirements.mdx b/src/content/partials/turnstile/security-requirements.mdx new file mode 100644 index 000000000000000..7a31837435e6e89 --- /dev/null +++ b/src/content/partials/turnstile/security-requirements.mdx @@ -0,0 +1,9 @@ +--- +{} + +--- + + +- Server-side validation is mandatory. It is critical to enforce Turnstile tokens with the Siteverify API. The Turnstile token could be invalid, expired, or already redeemed. Not verifying the token will leave major vulnerabilities in your implementation. You must call Siteverify to complete your Turnstile configuration. Otherwise, it is incomplete and will result in zeroes for token validation when viewing your metrics in [Turnstile Analytics](/turnstile/turnstile-analytics/). + +- Tokens expire after 300 seconds (5 minutes). Each token can only be validated once. Expired or used tokens must be replaced with fresh challenges. diff --git a/src/content/partials/turnstile/test-secret-key.mdx b/src/content/partials/turnstile/test-secret-key.mdx new file mode 100644 index 000000000000000..67d4fb5139a1840 --- /dev/null +++ b/src/content/partials/turnstile/test-secret-key.mdx @@ -0,0 +1,8 @@ +--- +{} + +--- + +You can test the dummy token generated with testing sitekey via Siteverify API with the testing secret key. Your production secret keys will reject dummy tokens. + +Refer to [Testing](/turnstile/troubleshooting/testing/) for more information. \ No newline at end of file diff --git a/src/content/partials/turnstile/test-sitekey.mdx b/src/content/partials/turnstile/test-sitekey.mdx new file mode 100644 index 000000000000000..5ecb3a3c4062605 --- /dev/null +++ b/src/content/partials/turnstile/test-sitekey.mdx @@ -0,0 +1,8 @@ +--- +{} + +--- + +You can test your Turnstile widget on your webpage without triggering an actual Cloudflare Challenge by using a testing sitekey. + +Refer to [Testing](/turnstile/troubleshooting/testing/) for more information. \ No newline at end of file diff --git a/src/content/partials/turnstile/widget-components.mdx b/src/content/partials/turnstile/widget-components.mdx new file mode 100644 index 000000000000000..36da69ec11fc53f --- /dev/null +++ b/src/content/partials/turnstile/widget-components.mdx @@ -0,0 +1,12 @@ +--- +{} + +--- + +Each widget gets its own unique sitekey and secret key pair, and options for configurations. + +| Component | Description | +| --- | --- | +| Sitekey | Public key used to invoke the Turnstile widget on your site. | +| Secret key | Private key used for server-side token validation. | +| Configurations | Mode, hostnames, appearance settings, and other options. | diff --git a/src/content/partials/turnstile/widget-management-overview.mdx b/src/content/partials/turnstile/widget-management-overview.mdx new file mode 100644 index 000000000000000..163322062279ee2 --- /dev/null +++ b/src/content/partials/turnstile/widget-management-overview.mdx @@ -0,0 +1,4 @@ +--- +{} + +--- diff --git a/src/content/partials/turnstile/widget-size.mdx b/src/content/partials/turnstile/widget-size.mdx deleted file mode 100644 index b7a575734f5ffd2..000000000000000 --- a/src/content/partials/turnstile/widget-size.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -{} - ---- - -| Size | Width | Height | -| ------- | ----- | ------ | -| Normal | 300px | 65px | -| Flexible | 100% (min: 300px) | 65px | -| Compact | 150px | 140px | \ No newline at end of file diff --git a/src/content/plans/index.json b/src/content/plans/index.json index fabb9823c9935fb..c472633901a9cf7 100644 --- a/src/content/plans/index.json +++ b/src/content/plans/index.json @@ -2138,7 +2138,7 @@ "title": "Pricing", "link": "https://www.cloudflare.com/application-services/products/turnstile/#Page-Pricing-AS", "free": "Free", - "ent": "Contact sales" + "ent": "Contact Sales" }, "widgets": { "title": "Number of widgets", @@ -2146,6 +2146,16 @@ "free": "Up to 20 widgets", "ent": "Unlimited" }, + "widget_types": { + "title": "All widget types", + "free": "Yes", + "ent": "Yes" + }, + "challenges_limit": { + "title": "Unlimited challenges (traffic or verification requests)", + "free": "Yes", + "ent": "Yes" + }, "hostname_management": { "title": "Hostname management", "link": "/turnstile/concepts/hostname-management/", @@ -2179,6 +2189,16 @@ "title": "Offlabel (remove Cloudflare branding)", "free": "No", "ent": "Yes" + }, + "compliance": { + "title": "WCAG 2.1 AA compliance", + "free": "Yes", + "ent": "Yes" + }, + "community": { + "title": "Community support", + "free": "Yes", + "ent": "Yes" } } }