diff --git a/src/content/docs/turnstile/additional-configuration/ephemeral-id.mdx b/src/content/docs/turnstile/additional-configuration/ephemeral-id.mdx
index 97079f37580164..8289deaeb7288c 100644
--- a/src/content/docs/turnstile/additional-configuration/ephemeral-id.mdx
+++ b/src/content/docs/turnstile/additional-configuration/ephemeral-id.mdx
@@ -6,18 +6,64 @@ sidebar:
---
-Ephemeral IDs generate a unique short-lived ID that can link behavior to a specific client instead of an IP address without relying on setting any cookies or using similar client-side storage.
+Ephemeral IDs provide device fingerprinting capabilities to enhance bot detection and abuse prevention beyond traditional IP-based methods. Ephemeral IDs are unique, short-lived identifiers that Turnstile generates to link visitor behavior to a specific client device without relying on cookies or client-side storage. They are temporary device fingerprints that help identify patterns of behavior across multiple interactions. Ephemeral ID is an advanced detection method that persists even when attackers change their IP addresses.
-When the same visitor interacts with Turnstile widgets from different Cloudflare customers, they receive different Ephemeral IDs for each contact. In attacks where fraudsters attempt to disguise themselves using different IP addresses, Ephemeral IDs detect abuse patterns more accurately than determining whether the visitor is a human or a bot.
+## Process
-Ephemeral IDs are not unique and have a lifespan of up to a few days. They can be useful for identifying a bad actor in acute attacks such as sudden spikes in fake account creations or credential stuffing.
+Ephemeral IDs use advanced analysis techniques to create unique identifiers that help find patterns of behavior across multiple interactions. These identifiers are generated dynamically for each visitor interaction with Turnstile widgets.
+
+No cookies or local storage is required.
+
+Ephemeral IDs are scoped to your Cloudflare account and cannot be shared across accounts. IDs have a short lifespan and expire within a few days. They cannot be used to identify individual users
+
+Ephemeral IDs provide more accurate abuse detection than IP-based methods by linking behavior to individual clients rather than network addresses. This is particularly effective against acute attacks where bad actors try to evade detection, such as credential stuffing and fake account creation attacks.
Refer to the [blog post](https://blog.cloudflare.com/turnstile-ephemeral-ids-for-fraud-detection/) for more information.
-## Availability
+---
+
+## Implementation
+
+### Enable Ephemeral IDs
+
+1. Contact your Cloudflare account team to enable Ephemeral ID entitlement for your account. This feature requires Enterprise-level access and cannot be self-activated.
+2. After entitlement is enabled, activate Ephemeral IDs for specific widgets using the Cloudflare API.
-Ephemeral IDs are available to Enterprise Bot Management customers with the Enterprise Turnstile add-on or standalone Enterprise Turnstile customers. Contact your account team for access to Ephemeral IDs.
+ ```bash title="cURL command" wrap
+ curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/challenges/widgets/$WIDGET_ID" \
+ -H "Authorization: Bearer $API_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "ephemeral_id": true
+ }'
+ ```
+3. Confirm Ephemeral IDs are active by checking your widget configuration.
-## Enablement
+ ```bash title="cURL command" wrap
+ curl -X GET "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/challenges/widgets/$WIDGET_ID" \
+ -H "Authorization: Bearer $API_TOKEN"
+ ```
+
+### Access Ephemeral IDs
+
+Once enabled, Ephemeral IDs are included in Siteverify API responses.
+
+```json title="Siteverify API response" wrap
+{
+ "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"
+ }
+}
+```
+
+---
+
+## Availability
-After your account team enables the Ephemeral ID entitlement, you must turn it on for the widget that you want the Ephemeral ID on via the [API call](/api/resources/turnstile/subresources/widgets/methods/update/).
\ No newline at end of file
+Ephemeral IDs are available to Enterprise Bot Management customers with the Enterprise Turnstile add-on or standalone Enterprise Turnstile customers. Contact your account team for access to Ephemeral IDs.
\ No newline at end of file
diff --git a/src/content/docs/turnstile/additional-configuration/hostname-management.mdx b/src/content/docs/turnstile/additional-configuration/hostname-management.mdx
deleted file mode 100644
index d8cc206d7555b9..00000000000000
--- a/src/content/docs/turnstile/additional-configuration/hostname-management.mdx
+++ /dev/null
@@ -1,79 +0,0 @@
----
-title: Hostname management
-pcx_content_type: reference
-sidebar:
- order: 2
-
----
-
-import { DashButton, Steps } from "~/components"
-
-You can associate hostnames with your widget to control where it can be used using Hostname Management. Managing your hostnames ensures that Turnstile works seamlessly with your setup, whether you add standalone hostnames or leverage zones registered to your Cloudflare account.
-
-## Hostname limits
-
-By default, a widget can have a maximum of 15 hostnames for Free users and 200 hostnames for Enterprise customers. Each widget requires at least one hostname to be entered. You will not be able to create the widget without a hostname configured.
-
-### Availability
-
-Customers with Enterprise Bot Management or Enterprise Turnstile may create and use a widget without entering any hostnames, or have up to 200 hostnames associated with a widget. Contact your account team for access to this feature.
-
-## Add a custom hostname
-
-You can add a hostname to your Turnstile widget even if it is not on the Cloudflare network or registered as a zone. There are no prerequisites for using Turnstile.
-
-To add a custom hostname:
-
-
- 1. In the Cloudflare dashboard, go to the **Turnstile** page.
-
-
- 2. On an existing widget, select **Settings**.
- 3. Select **Add Hostnames** under Hostname Management.
- 4. Add a custom hostname or choose from an existing hostname.
- 5. Select **Add**.
-
-
-## Add hostnames with a registered zone
-
-If you already have a zone registered with Cloudflare, you can add hostnames during the Turnstile widget setup. You will see all zones registered to your account, where you can select the relevant hostname from the list, and it will be added to your Turnstile widget seamlessly.
-
-## Hostname requirements
-
-:::caution
-Customers enabling [client-side rendering](/turnstile/get-started/client-side-rendering/) must validate their hostnames by looking at the [`hostname`](/turnstile/get-started/server-side-validation/#:~:text=challenge%20was%20solved.-,hostname,-is%20the%20hostname) field in the Siteverify response.
-:::
-
-When associating hostnames with a widget, follow these requirements:
-
-- Hostnames must be fully qualified domain names (FQDNs), such as `example.com` or `subdomain.example.com`.
-- Wildcards are not supported. Specify each hostname you want Turnstile to work on.
-- The hostname should not include:
- - A scheme (for example, `http://` or `https://`)
- - A port (for example, `443`)
- - A path (for example, `/path`)
-
-### Subdomain specification
-
-Specifying a subdomain is optional, but it can be used to further restrict the widget. For example, adding `www.example.com` as a hostname will allow widgets to work on:
-
-- `www.example.com`
-- `abc.www.example.com:8080`
-
-However, it will not work on the following hostnames:
-
-- `example.com`
-- `dash.example.com`
-- `cloudflare.com`
-
-:::note
-If the widget is embedded on a hostname not listed, it will display an error message.
-:::
-
-## Optional hostname validation (Enterprise only)
-
-Customers with Enterprise Bot Management or Enterprise Turnstile can have the optional `any hostname` validation entitlement.
-
-By default, a widget requires at least one hostname to be entered. With this feature, you can create and use a widget without entering any hostnames for the widget.
-
-Contact your account team to enable this feature.
\ No newline at end of file
diff --git a/src/content/docs/turnstile/additional-configuration/hostname-management/any-hostname.mdx b/src/content/docs/turnstile/additional-configuration/hostname-management/any-hostname.mdx
new file mode 100644
index 00000000000000..22152af7d9fcf4
--- /dev/null
+++ b/src/content/docs/turnstile/additional-configuration/hostname-management/any-hostname.mdx
@@ -0,0 +1,73 @@
+---
+title: Any Hostname (Enterprise only)
+pcx_content_type: reference
+sidebar:
+ order: 4
+ label: Any Hostname
+
+---
+
+The Any Hostname feature removes the requirement to specify hostnames during widget creation, allowing widgets to function on any domain.
+
+By default, hostname validation is a security feature that prevents unauthorized use of your widgets. The Any Hostname entitlement removes this restriction, making the hostname field optional during widget creation.
+
+When enabled, widgets can be created without the required hostname specification and used on any domain without pre-configuration. Hostname validation protection is also removed.
+
+## Implementation
+
+To reduce security risks when using Any Hostname, monitor widget usage through [Turnstile Analytics](/turnstile/turnstile-analytics/) to identify unexpected patterns, implement server-side validation with hostname checking in your application code, and use `action` and `cData` parameters to track widget usage sources and identify where widgets are being deployed.
+
+When using the Any Hostname feature, it is essential to implement additional validation in your server-side code to maintain security controls. Always validate the `hostname` field in Siteverify responses.
+
+```js title="Example response"
+async function validateTurnstileWithHostname(token, expectedHostnames = []) {
+ const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ secret: process.env.TURNSTILE_SECRET,
+ response: token
+ })
+ });
+
+ const result = await response.json();
+
+ if (!result.success) {
+ return { valid: false, error: 'Token validation failed' };
+ }
+
+ // Additional hostname validation when using Any Hostname
+ if (expectedHostnames.length > 0 && !expectedHostnames.includes(result.hostname)) {
+ return {
+ valid: false,
+ error: 'Hostname not in allowed list',
+ hostname: result.hostname
+ };
+ }
+
+ return { valid: true, data: result };
+}
+```
+
+You should regularly review Turnstile Analytics for unexpected usage patterns and monitor the hostname field in Siteverify responses. You can set up alerts for widget usage on unexpected domains.
+
+Use `action` and `cData` parameters to track widget usage sources.
+
+```html
+
+
+```
+
+---
+
+## Use cases
+
+The Any Hostname feature is particularly valuable for customers with:
+
+- Large domain portfolios with many domains to manage individually.
+- Dynamic subdomain creation and frequently create subdomains or customer-specific domains.
+- Multi-tenant applications such as SaaS platforms serving multiple customer domains.
+- Development environments that test across various staging and development domains.
\ No newline at end of file
diff --git a/src/content/docs/turnstile/additional-configuration/hostname-management/index.mdx b/src/content/docs/turnstile/additional-configuration/hostname-management/index.mdx
new file mode 100644
index 00000000000000..735f0431205531
--- /dev/null
+++ b/src/content/docs/turnstile/additional-configuration/hostname-management/index.mdx
@@ -0,0 +1,100 @@
+---
+title: Hostname management
+pcx_content_type: reference
+sidebar:
+ order: 2
+
+---
+
+import { DashButton, Steps, Tabs, TabItem, Details } from "~/components"
+
+Hostname management controls where your Turnstile widgets can be used by specifying which domains are authorized to load and execute your widgets. This security measure prevents unauthorized use of your widgets on domains that you do not control.
+
+You can associate hostnames with your widget to control where it can be used via Hostname Management. Managing your hostnames ensures that Turnstile works seamlessly with your setup, whether you add standalone hostnames or leverage zones registered to your Cloudflare account.
+
+---
+
+## Hostname requirements
+
+### Standard configuration
+
+By default, every widget requires at least one hostname to be configured. You cannot create a widget without specifying at least one authorized hostname.
+
+### Hostname format requirements
+
+When adding hostnames, follow these requirements:
+
+- The hostname must be fully qualified domain names (FQDNs): `example.com` or `subdomain.example.com`
+- No wildcards are supported. You must specify each hostname individually.
+
+:::caution[Invalid formats]
+The following formats are not valid and will not be accepted:
+
+- Schemes such as `http://example.com` or `https://example.com`
+- Ports such as `example.com:443` or `subdomain.example.com:8080`
+- Paths such as `example.com/path` or `subdomain.example.com/login`
+:::
+
+### Subdomain behavior
+
+Specifying a subdomain provides additional security by restricting widget usage.
+
+For example, adding `www.example.com` as a hostname will allow widgets to work on:
+
+- `www.example.com`
+- `abc.www.example.com:8080` (subdomains of the specified hostname).
+
+However, it will not work on:
+
+- `example.com` (parent domain)
+- `dash.example.com` (sibling subdomain)
+- `cloudflare.com` (unrelated domain)
+
+## Add hostnames
+
+
+
+
+
+ 1. In the Cloudflare dashboard, go to the **Turnstile** page.
+
+
+ 2. Select an existing widget.
+ 3. Go to **Settings**.
+ 4. Under **Hostname Management**, select **Add Hostnames**.
+ 5. Add a custom hostname or choose from an existing hostname.
+ 6. Select **Add**.
+
+
+
+
+ 1. In the Cloudflare dashboard, go to the **Turnstile** page.
+
+
+ 2. Select **Add widget**.
+ 3. In the hostname field, enter your domain(s).
+ 4. If you have zones registered with Cloudflare, you can select from existing zones
+
+
+
+
+
+ ```bash title="cURL command" wrap
+ curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/challenges/widgets/$WIDGET_ID" \
+ -H "Authorization: Bearer $API_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "domains": ["example.com", "app.example.com", "api.example.com"]
+ }'
+ ```
+
+
+
+
+---
+
+## Limitations
+
+Free users are entitled to a maximum of 15 hostnames per widget.
+
+Enterprise customers can have up to 200 hostnames per widget.
\ No newline at end of file
diff --git a/src/content/docs/turnstile/additional-configuration/hostname-management/pre-clearance.mdx b/src/content/docs/turnstile/additional-configuration/hostname-management/pre-clearance.mdx
new file mode 100644
index 00000000000000..49ccf29517cc44
--- /dev/null
+++ b/src/content/docs/turnstile/additional-configuration/hostname-management/pre-clearance.mdx
@@ -0,0 +1,29 @@
+---
+title: Pre-clearance configuration
+pcx_content_type: reference
+sidebar:
+ order: 3
+
+---
+
+[Pre-clearance](/cloudflare-challenges/concepts/clearance/#pre-clearance-support-in-turnstile) allows Turnstile to issue clearance cookies that can be used across your Cloudflare-protected domains. This feature requires specific hostname configuration for proper functionality.
+
+## Prerequisites
+
+For pre-clearance to work correctly, you must:
+
+1. Use a registered Cloudflare zone.
+
+ The hostname must be a zone registered in your Cloudflare account. When configuring your widget via the dashboard, you can select from existing zones.
+
+2. Select the registered Cloudflare zone with intended WAF rule to set pre-clearance.
+
+ The zone you select must contain the WAF rule you wish to set pre-clearance through Turnstile.
+
+ For example, if you have `example.com` and `app.example.com` as registered zones and you want to have Turnstile issue pre-clearance for `app.example.com`, you must select `app.example.com`.
+
+## Validation
+
+The clearance cookie `cf_clearance` will only be accepted on domains that match the widget's configured hostnames, are registered as zones in your Cloudflare account, and have challenges enabled through Cloudflare's security settings.
+
+If pre-clearance is configured incorrectly, clearance cookies may become invalid and lead to additional challenge requests.
\ No newline at end of file
diff --git a/src/content/docs/turnstile/additional-configuration/offlabel.mdx b/src/content/docs/turnstile/additional-configuration/offlabel.mdx
new file mode 100644
index 00000000000000..343b13a6d9c578
--- /dev/null
+++ b/src/content/docs/turnstile/additional-configuration/offlabel.mdx
@@ -0,0 +1,69 @@
+---
+title: Remove Cloudflare branding with Offlabel
+pcx_content_type: reference
+sidebar:
+ order: 4
+ label: Offlabel
+---
+
+Offlabel is an Enterprise-only feature that removes Cloudflare branding and logo from Turnstile widgets. When enabled, widgets display without any visual references to Cloudflare, allowing for a seamless integration with your brand identity.
+
+When Offlabel is enabled:
+
+- The Cloudflare logo and color schemes are removed from all widget states.
+- The widget maintains the same functionality, behavior, and WCAG 2.1 AA accessibility compliance.
+- All security features remain unchanged.
+
+The widget will display with a clean, unbranded appearance that integrates seamlessly with your website's design.
+
+---
+
+## Implementation
+
+### Enable Offlabel
+
+After your account team enables the Offlabel entitlement, you can activate it for specific widgets using the Cloudflare API.
+
+ ```bash title="cURL command" wrap
+ curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/challenges/widgets/$WIDGET_ID" \
+ -H "Authorization: Bearer $API_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "offlabel": true
+ }'
+ ```
+
+### Create new widgets with Offlabel
+
+You can enable Offlabel when creating new widgets.
+
+ ```bash title="cURL command" wrap
+ curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/challenges/widgets" \
+ -H "Authorization: Bearer $API_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "name": "Branded Widget",
+ "domains": ["example.com"],
+ "mode": "managed",
+ "offlabel": true
+ }'
+ ```
+
+### Verification
+
+Confirm Offlabel is enabled by checking your widget configuration.
+
+ ```bash title="cURL command" wrap
+ curl -X GET "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/challenges/widgets/$WIDGET_ID" \
+ -H "Authorization: Bearer $API_TOKEN"
+ ```
+
+The response will include `"offlabel": true` when the feature is active.
+
+---
+
+## Availability
+
+Offlabel is available exclusively to Enterprise customers with the Enterprise Turnstile add-on or Standalone Enterprise Turnstile customers.
+
+Contact your account team for access to the Offlabel feature.
\ No newline at end of file
diff --git a/src/content/docs/turnstile/concepts/widget.mdx b/src/content/docs/turnstile/concepts/widget.mdx
index 3025a0de5fe8cb..06dfba62a27c63 100644
--- a/src/content/docs/turnstile/concepts/widget.mdx
+++ b/src/content/docs/turnstile/concepts/widget.mdx
@@ -61,26 +61,17 @@ Refer to [Widget configurations](/turnstile/get-started/client-side-rendering/wi
### Normal operation states
-- **Loading**: Widget is processing the challenge.
-- **Interaction**: Visitor needs to check the box (Managed mode only).
-- **Success**: The Challenge was completed successfully.
+```mermaid
+flowchart LR
+ A[Loading
Widget is processing the challenge. ] --> B[Interaction*
Visitor needs to check the box.
*Managed mode only.]
+ B --> C[Success
The Challenge was completed successfully.]
+```
### Error states
-#### Unknown error
-
-When an unknown error occurs during the challenge, visitors will encounter this widget state. Visitors can follow the troubleshooting guidelines from the widget or refresh the page to retry the challenge.
-
-#### Interaction timed out
-
-When the visitor is presented with a checkbox but does not interact with it for an extended period of time. The challenge must be reissued by reloading the page or the widget.
-
-#### Challenge timed out
-
-When the verification was completed but no further action has been taken, the challenge outcome will no longer be valid. For example, if a Turnstile widget is on a login page and the Turnstile successfully ran, but the visitor did not log in for an extended period of time, the challenge must be reissued by reloading the page or the widget.
-
-#### Outdated or unsupported browser
-
-Visitors with outdated browsers or unsupported browsers will encounter this widget state.
-
-Refer to [Supported browsers](/cloudflare-challenges/reference/supported-browsers/) for more information regarding supported browsers.
+| Type
| Description |
+| ---- | ----------- |
+| Unknown error | When an unknown error occurs during the challenge, visitors will encounter this widget state. Visitors can follow the troubleshooting guidelines from the widget or refresh the page to retry the challenge. |
+| Interaction timed out | When the visitor is presented with a checkbox but does not interact with it for an extended period of time. The challenge must be reissued by reloading the page or the widget. |
+| Challenge timed out | When the verification was completed but no further action has been taken, the challenge outcome will no longer be valid. For example, if a Turnstile widget is on a login page and the Turnstile successfully ran, but the visitor did not log in for an extended period of time, the challenge must be reissued by reloading the page or the widget. |
+| Outdated or unsupported browser | Visitors with outdated browsers or unsupported browsers will encounter this widget state. Refer to [Supported browsers](/cloudflare-challenges/reference/supported-browsers/) for more information regarding supported browsers. |
diff --git a/src/content/docs/turnstile/get-started/widget-management/api.mdx b/src/content/docs/turnstile/get-started/widget-management/api.mdx
index b88d127faba035..eef40b7bcfb42b 100644
--- a/src/content/docs/turnstile/get-started/widget-management/api.mdx
+++ b/src/content/docs/turnstile/get-started/widget-management/api.mdx
@@ -1,8 +1,9 @@
---
-title: API
+title: Create and manage widgets using Cloudflare API
pcx_content_type: how-to
sidebar:
order: 2
+ label: API
---
import { APIRequest } from "~/components"
diff --git a/src/content/docs/turnstile/get-started/widget-management/dashboard.mdx b/src/content/docs/turnstile/get-started/widget-management/dashboard.mdx
index 85e3b24f8d31a6..32264b323634ae 100644
--- a/src/content/docs/turnstile/get-started/widget-management/dashboard.mdx
+++ b/src/content/docs/turnstile/get-started/widget-management/dashboard.mdx
@@ -1,8 +1,9 @@
---
-title: Cloudflare dashboard
+title: Create and manage widgets using the Cloudflare dashboard
pcx_content_type: how-to
sidebar:
order: 1
+ label: Cloudflare dashboard
---
import { Steps, DashButton } from "~/components";
diff --git a/src/content/docs/turnstile/get-started/widget-management/terraform.mdx b/src/content/docs/turnstile/get-started/widget-management/terraform.mdx
index 5fce491be24008..04c9787c6f7584 100644
--- a/src/content/docs/turnstile/get-started/widget-management/terraform.mdx
+++ b/src/content/docs/turnstile/get-started/widget-management/terraform.mdx
@@ -1,8 +1,9 @@
---
-title: Terraform
+title: Create and manage widgets using Terraform
pcx_content_type: how-to
sidebar:
order: 3
+ label: Terraform
---
import { Render } from "~/components"
diff --git a/src/content/docs/turnstile/troubleshooting/testing.mdx b/src/content/docs/turnstile/troubleshooting/testing.mdx
index 97bb7fe6c43576..4fdbfdab255481 100644
--- a/src/content/docs/turnstile/troubleshooting/testing.mdx
+++ b/src/content/docs/turnstile/troubleshooting/testing.mdx
@@ -1,41 +1,156 @@
---
-title: Testing
+title: Test your Turnstile implementation
pcx_content_type: reference
sidebar:
order: 1
+ label: Testing
---
import { GlossaryTooltip } from "~/components"
-## Dummy sitekeys and secret keys
+Use dummy sitekeys and secret keys to test your Turnstile implementation without triggering real challenges that would interfere with automated testing suites.
-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.
+Automated testing suites (like Selenium, Cypress, or Playwright) are detected as bots by Turnstile, which can cause:
-To test locally with real keys, you need to add your testing hostnames (like `localhost`) to your [domain allowlist](/turnstile/additional-configuration/hostname-management/).
+- Tests to fail when Turnstile blocks automated browsers
+- Unpredictable test results due to challenge variations
+- Interference with form submission testing
+- Difficulty testing complete user flows
-Dummy sitekeys can be used from any domain, including on `localhost`.
+Dummy keys solve this by providing predictable, controlled responses that work with automated testing tools.
-Cloudflare recommends that sitekeys used in production do not allow local domains (`localhost`, `127.0.0.1`), but users can choose to add local domains to the list of allowed domains.
+## Test sitekeys
-| Sitekey | Description | Visibility |
-| -------------------------- | ------------------------------- | ---------- |
-| `1x00000000000000000000AA` | Always passes | visible |
-| `2x00000000000000000000AB` | Always blocks | visible |
-| `1x00000000000000000000BB` | Always passes | invisible |
-| `2x00000000000000000000BB` | Always blocks | invisible |
-| `3x00000000000000000000FF` | Forces an interactive challenge | visible |
+| Sitekey | Behavior | Widget Type | Use case |
+| ------- | -------- | ----------- | -------- |
+| `1x00000000000000000000AA` | Always passes | Visible | Test successful form submissions |
+| `2x00000000000000000000AB` | Always fails | Visible | Test error handling and retry logic |
+| `1x00000000000000000000BB` | Always passes | Invisible | Test invisible widget success flows |
+| `2x00000000000000000000BB` | Always fails | Invisible | Test invisible widget error handling |
+| `3x00000000000000000000FF` | Forces interactive challenge | Visible | Test user interaction scenarios |
-These dummy sitekeys will produce the `XXXX.DUMMY.TOKEN.XXXX` dummy response token.
+## Test secret keys
-Production secret keys will reject this token. You must also use a dummy secret key for testing purposes.
+Use these secret keys for server-side validation testing:
-| Secret key | Description |
-| ------------------------------------- | ------------------------------------ |
-| `1x0000000000000000000000000000000AA` | Always passes |
-| `2x0000000000000000000000000000000AA` | Always fails |
-| `3x0000000000000000000000000000000AA` | Yields a "token already spent" error |
+| Secret key | Behavior | Use case |
+| ---------- | -------- | -------- |
+| `1x0000000000000000000000000000000AA` | Always passes validation | Test successful token validation |
+| `2x0000000000000000000000000000000AA` | Always fails validation | Test validation error handling |
+| `3x0000000000000000000000000000000AA` | Returns "token already spent" error | Test duplicate token handling |
-Dummy secret keys will only accept the `XXXX.DUMMY.TOKEN.XXXX` dummy response token.
+---
+
+## Implementation
+
+### Local development
+
+Test keys work on any domain, including:
+
+- `localhost`
+- `127.0.0.1`
+- `0.0.0.0`
+- Any development domain
+
+Cloudflare recommends that sitekeys used in production do not allow local domains (`localhost` or `127.0.0.1`), but users can choose to add local domains to the list of allowed domains under [Hostname Management](/turnstile/additional-configuration/hostname-management/). Dummy sitekeys can be used from any domain, including on `localhost`.
+
+### Client-side testing
+
+Replace your production sitekey with a test sitekey.
+
+```html
+
+
+
+
+
+```
+
+### Server-side testing
+
+Replace your production secret key with a test secret key.
+
+```js
+// Environment-based configuration
+const SECRET_KEY = process.env.NODE_ENV === 'production'
+ ? process.env.TURNSTILE_SECRET_KEY
+ : '1x0000000000000000000000000000000AA';
+
+// Use in validation
+const validation = await validateTurnstile(token, SECRET_KEY);
+```
+
+### Environment configuration
+
+Set up different keys for different environments.
+
+```shell
+
+# .env.development
+TURNSTILE_SITEKEY=1x00000000000000000000AA
+TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA
+
+# .env.test
+TURNSTILE_SITEKEY=2x00000000000000000000AB
+TURNSTILE_SECRET_KEY=2x0000000000000000000000000000000AA
+
+# .env.production
+TURNSTILE_SITEKEY=your-real-sitekey
+TURNSTILE_SECRET_KEY=your-real-secret-key
+```
+
+---
+
+## Dummy token behavior
+
+### Token generation
+
+Test sitekeys generate a dummy token: `XXXX.DUMMY.TOKEN.XXXX`
+
+### Token validation
+
+- Test secret keys: Only accept the dummy token, reject real tokens.
+- Production secret keys: Only accept real tokens, reject dummy tokens.
+
+:::note
+Production secret keys will reject the dummy token. You must also use a dummy secret key for testing purposes.
+:::
+
+### Validation response
+
+```json title="Success response"
+{
+ "success": true,
+ "challenge_ts": "2022-02-28T15:14:30.096Z",
+ "hostname": "localhost",
+ "error-codes": [],
+ "action": "test",
+ "cdata": "test-data"
+}
+```
+
+```json title="Failure response"
+{
+ "success": false,
+ "error-codes": ["invalid-input-response"]
+}
+
+```
+
+```json title="Token already redeemed"
+{
+ "success": false,
+ "error-codes": ["timeout-or-duplicate"]
+}
+```
+
+---
+
+## Testing scenarios
-If you pass a real response, it will fail to prevent common misconfigurations.
+| Test sitekey | Test secret key | Test case
|
+| ------------ | --------------- | --------- |
+| `1x00000000000000000000AA` | `1x0000000000000000000000000000000AA` | This combination will always result in successful validation. |
+| `2x00000000000000000000AB` | `2x0000000000000000000000000000000AA` | This combination will always fail. |
+| `1x00000000000000000000AA` | `3x0000000000000000000000000000000AA` | This combination will always fail with "timeout-or-duplicate" error. |
\ No newline at end of file