From 47a282e73d9fad48344c4a1dda42b0a9eb1ab1a4 Mon Sep 17 00:00:00 2001
From: jdv
Date: Mon, 15 Jun 2026 22:37:41 +0200
Subject: [PATCH 1/5] rough version
---
crowdsec-docs/sidebarsUnversioned.ts | 5 +
.../bouncers/cloudflare-worker-installer.mdx | 129 ++++++++++++++++++
.../bouncers/cloudflare-workers.mdx | 6 +-
3 files changed, 139 insertions(+), 1 deletion(-)
create mode 100644 crowdsec-docs/unversioned/bouncers/cloudflare-worker-installer.mdx
diff --git a/crowdsec-docs/sidebarsUnversioned.ts b/crowdsec-docs/sidebarsUnversioned.ts
index ce6abed26..5a31c100c 100644
--- a/crowdsec-docs/sidebarsUnversioned.ts
+++ b/crowdsec-docs/sidebarsUnversioned.ts
@@ -660,6 +660,11 @@ const sidebarsUnversionedConfig: SidebarConfig = {
label: "Cloudflare Workers",
id: "bouncers/cloudflare-workers",
},
+ {
+ type: "doc",
+ label: "Cloudflare Worker Installer (GUI)",
+ id: "bouncers/cloudflare-worker-installer",
+ },
{
type: "doc",
label: "Custom",
diff --git a/crowdsec-docs/unversioned/bouncers/cloudflare-worker-installer.mdx b/crowdsec-docs/unversioned/bouncers/cloudflare-worker-installer.mdx
new file mode 100644
index 000000000..a031d99d2
--- /dev/null
+++ b/crowdsec-docs/unversioned/bouncers/cloudflare-worker-installer.mdx
@@ -0,0 +1,129 @@
+---
+id: cloudflare-worker-installer
+title: Cloudflare Worker Bouncer — Web Installer
+---
+
+import useBaseUrl from '@docusaurus/useBaseUrl';
+import RemediationSupportBadges from '@site/src/components/remediation-support-badge';
+
+
+
+
+
+
+
+
+
+
+
+
+:::tip Recommended for Autonomous Mode
+Fully self hosted on Cloudflare it's the easiest way to deploy CrowdSec Blocklists on Cloudflare
+(no persistent daemon, plugged directly to a [Blocklist as a Service integration](/u/integrations/remediationcomponent)), this installer is the easiest way to get started.
+
+For **Daemon Mode** (Security Engine + Go process), use the [CLI-based installation](/u/bouncers/cloudflare-workers#installation) instead.
+:::
+
+## Overview
+
+The **CrowdSec Cloudflare Worker Bouncer Installer** is a web-based GUI that lets you deploy and manage the [Cloudflare Worker Bouncer](/u/bouncers/cloudflare-workers) in **Autonomous Mode** — directly from your browser, with no command-line required.
+
+It runs as a Cloudflare Worker itself and communicates with the Cloudflare API on your behalf to:
+
+- Verify your API token and auto-load your accounts and zones
+- Set up (or update) the CrowdSec BLaaS integration endpoint
+- Install or remove the bouncer on a per-zone basis
+- Configure Turnstile (CAPTCHA) challenges per zone
+
+## How it Works
+
+The installer is itself a Cloudflare Worker application (built with React Router + Hono). Once deployed to your Cloudflare account, it provides a step-by-step UI:
+
+1. **Cloudflare API Token** — paste your token; the installer validates it and confirms permissions automatically.
+2. **CrowdSec Integration Endpoint** — paste your BLaaS (Blocklist as a Service) URL; the installer reads and displays the currently configured endpoint.
+3. **Zone Protection** — browse your zones, see which are protected and which are not, and install or remove the bouncer per zone with one click.
+
+No YAML config files, no CLI flags.
+
+### Deployed Resources
+
+When you install the bouncer on a zone, the installer creates the following resources in your Cloudflare account:
+
+| Resource | Name | Purpose |
+|---|---|---|
+| KV Namespace | `CROWDSECCFBOUNCERNS` | Stores decisions and configuration |
+| Main Worker | `crowdsec-cloudflare-worker-bouncer` | Applies decisions to incoming requests |
+| Sync Worker | `crowdsec-decisions-sync-worker` | Periodically fetches decisions from CrowdSec BLaaS |
+| Cron Trigger | Every 5 min (default) | Schedules the sync worker |
+| Worker Routes | Per zone | Routes traffic through the bouncer |
+| Turnstile Widgets | Per zone | Powers CAPTCHA challenges |
+
+## Prerequisites
+
+- A Cloudflare account with at least one zone
+- A **Cloudflare API Token** with the [required permissions](/u/bouncers/cloudflare-workers#generating-a-cloudflare-api-token)
+- A **CrowdSec BLaaS Integration Endpoint** — set up via the [CrowdSec Console](/u/integrations/remediationcomponent)
+
+## Deployment
+
+The installer is deployed with a single click via the Cloudflare deploy button:
+
+[](https://deploy.workers.cloudflare.com/?url=https://github.com/crowdsecurity/cs-cloudflare-worker-bouncer-install)
+
+This deploys the installer Worker to your Cloudflare account. Once deployed, open the Worker's URL in your browser to access the UI.
+
+:::info
+The installer Worker itself does not store your API token or credentials. Your token is used only within the current browser session and is never persisted.
+:::
+
+## Step-by-Step Guide
+
+### Step 1 — Cloudflare API Token
+
+Paste your Cloudflare API token in the first section. The installer verifies it immediately and confirms that all required permissions are in place.
+
+If you don't have a token yet, click **Create token ↗** to open the Cloudflare dashboard with the correct permissions pre-selected. See [Generating a Cloudflare API Token](/u/bouncers/cloudflare-workers#generating-a-cloudflare-api-token) for the full list of required permissions.
+
+### Step 2 — CrowdSec Integration Endpoint
+
+Paste your **BLaaS Integration Endpoint URL** from the [CrowdSec Console](https://app.crowdsec.net). The installer reads and displays the currently configured endpoint so you can verify it before proceeding.
+
+:::note
+The BLaaS endpoint is the URL of a Remediation Component integration created in the CrowdSec Console. It is used by the sync worker to fetch the list of IPs to block.
+See [Blocklist as a Service integration](/u/integrations/remediationcomponent) for setup instructions.
+:::
+
+### Step 3 — Zone Protection
+
+The installer lists all zones in your Cloudflare account and shows their current status:
+
+- **Protected** — the bouncer is installed and active on this zone
+- **Unprotected** — the bouncer is not installed on this zone
+
+Select one or more zones and click **Install** to deploy, or **Remove** to uninstall from that zone. You can manage zones individually without affecting the others.
+
+#### Captcha (Turnstile)
+
+When installing a zone, you can enable **Turnstile** to serve a CAPTCHA challenge instead of a hard block. The installer creates a Turnstile widget for each protected zone automatically.
+
+You can toggle Turnstile per zone from the Zone Protection section after installation.
+
+## Updating the BLaaS Endpoint
+
+If you change your CrowdSec integration endpoint, you can update it without reinstalling the bouncer. Simply paste the new endpoint in **Step 2** and click **Edit** — the installer updates the sync worker's credentials in the KV store, and the next sync cycle will use the new endpoint.
+
+## Removing the Bouncer
+
+To remove the bouncer from a zone, select the zone in **Step 3** and click **Remove**. This deletes the worker routes and Turnstile widget for that zone only — the shared KV namespace and worker scripts remain in place for other protected zones.
+
+To remove all bouncer infrastructure at once (KV, workers, all routes, all Turnstile widgets), click **Uninstall all** in the Zone Protection section.
+
+## Relationship with the Cloudflare Worker Bouncer
+
+The installer deploys the same workers as the [CLI-based Cloudflare Worker Bouncer](/u/bouncers/cloudflare-workers) in **Autonomous Mode**. The two methods are interchangeable — you can install with the GUI and manage with the CLI, or vice versa, as long as you use the same resource names (`CROWDSECCFBOUNCERNS`, `crowdsec-cloudflare-worker-bouncer`, `crowdsec-decisions-sync-worker`).
+
+For advanced configuration (custom KV namespace names, multiple bouncer instances on the same account, daemon mode, Prometheus metrics), refer to the full [Cloudflare Worker Bouncer documentation](/u/bouncers/cloudflare-workers).
+
+## Source
+
+The installer source code is available at [crowdsecurity/cs-cloudflare-worker-bouncer-install](https://github.com/crowdsecurity/cs-cloudflare-worker-bouncer-install).
diff --git a/crowdsec-docs/unversioned/bouncers/cloudflare-workers.mdx b/crowdsec-docs/unversioned/bouncers/cloudflare-workers.mdx
index 3e5031173..40dfb88a2 100644
--- a/crowdsec-docs/unversioned/bouncers/cloudflare-workers.mdx
+++ b/crowdsec-docs/unversioned/bouncers/cloudflare-workers.mdx
@@ -75,6 +75,10 @@ This mode is recommended if you want a **minimal footprint** deployment, without
*It can also be plugged to a Security Engine, but woould require an open connection from Cloudflare to your LAPI. We recommend using [Daemon mode](#daemon-mode) in a usecase where a Security Engine is involved.*
+:::tip Web Installer available
+Looking for a no-CLI setup? The **[Cloudflare Worker Bouncer Installer](/u/bouncers/cloudflare-worker-installer)** is a browser-based GUI that deploys the bouncer in Autonomous Mode with a step-by-step wizard — no YAML or command line required.
+:::
+
#### ⚙️ Init / Removal on CloudFlare
Creation of necessary **Cloudflare Workers** and **KV stores** is done once at init time.
@@ -423,7 +427,7 @@ Ensure the `token` you are generating is a **user** API token these are found vi
1. Sign in as a user who has access to the desired account.
-Then click [this link](https://dash.cloudflare.com/profile/api-tokens?permissionGroupKeys=%5B%7B%22key%22%3A%22account_settings%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22challenge_widgets%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22user_details%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22workers_kv_storage%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22workers_routes%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22workers_scripts%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22zone%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22dns%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22account_analytics%22%2C%22type%22%3A%22read%22%7D%5D&name=) and create the token.
+Then click [**this link**](https://dash.cloudflare.com/profile/api-tokens?permissionGroupKeys=%5B%7B%22key%22%3A%22account_settings%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22challenge_widgets%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22user_details%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22workers_kv_storage%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22workers_routes%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22workers_scripts%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22zone%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22dns%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22account_analytics%22%2C%22type%22%3A%22read%22%7D%5D&name=) and create the token.
Alternatively, you can go to [Tokens](https://dash.cloudflare.com/profile/api-tokens) and create the token.
From 7d00c6b5ead2b46e13228660de988064fa31e267 Mon Sep 17 00:00:00 2001
From: jdv
Date: Wed, 17 Jun 2026 23:20:36 +0200
Subject: [PATCH 2/5] first draft
---
crowdsec-docs/docusaurus.config.ts | 2 +
crowdsec-docs/sidebarsUnversioned.ts | 12 +-
.../bouncers/cloudflare-deprecated.mdx | 523 +++++++++++
.../bouncers/cloudflare-worker-installer.mdx | 129 ---
.../bouncers/cloudflare-workers.mdx | 841 ------------------
.../unversioned/bouncers/cloudflare.mdx | 835 +++++++++++------
6 files changed, 1081 insertions(+), 1261 deletions(-)
create mode 100644 crowdsec-docs/unversioned/bouncers/cloudflare-deprecated.mdx
delete mode 100644 crowdsec-docs/unversioned/bouncers/cloudflare-worker-installer.mdx
delete mode 100644 crowdsec-docs/unversioned/bouncers/cloudflare-workers.mdx
diff --git a/crowdsec-docs/docusaurus.config.ts b/crowdsec-docs/docusaurus.config.ts
index 0543214c5..fb3c0a9a8 100644
--- a/crowdsec-docs/docusaurus.config.ts
+++ b/crowdsec-docs/docusaurus.config.ts
@@ -238,6 +238,8 @@ const redirects = [
},
// redirecting old hidden invoice page to new faq billing
{ from: "/u/console/premium_upgrade/premium_invoices/", to: "/u/troubleshooting/billing_faq" },
+ // unified Cloudflare bouncer page, cloudflare-workers content moved to cloudflare page (and that one became deprecated)
+ { from: "/u/bouncers/cloudflare-workers", to: "/u/bouncers/cloudflare" },
];
function redirectsGlobalDataPlugin() {
diff --git a/crowdsec-docs/sidebarsUnversioned.ts b/crowdsec-docs/sidebarsUnversioned.ts
index 5a31c100c..5b11faf1e 100644
--- a/crowdsec-docs/sidebarsUnversioned.ts
+++ b/crowdsec-docs/sidebarsUnversioned.ts
@@ -650,20 +650,10 @@ const sidebarsUnversionedConfig: SidebarConfig = {
label: "BlockList Mirror",
id: "bouncers/blocklist-mirror",
},
- {
- type: "doc",
- label: "Cloudflare",
- id: "bouncers/cloudflare",
- },
{
type: "doc",
label: "Cloudflare Workers",
- id: "bouncers/cloudflare-workers",
- },
- {
- type: "doc",
- label: "Cloudflare Worker Installer (GUI)",
- id: "bouncers/cloudflare-worker-installer",
+ id: "bouncers/cloudflare",
},
{
type: "doc",
diff --git a/crowdsec-docs/unversioned/bouncers/cloudflare-deprecated.mdx b/crowdsec-docs/unversioned/bouncers/cloudflare-deprecated.mdx
new file mode 100644
index 000000000..01ceb7129
--- /dev/null
+++ b/crowdsec-docs/unversioned/bouncers/cloudflare-deprecated.mdx
@@ -0,0 +1,523 @@
+---
+id: cloudflare-deprecated
+title: Cloudflare DEPRECATED BOUNCER
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+import RemediationSupportBadges from '@site/src/components/remediation-support-badge';
+
+
+
+
+
+
+
+
+
+
+
+
+📚 Documentation
+💠 Hub
+💬 Discourse
+
+
+
+
+:::danger
+
+This bouncer isn't actively supported anymore, due to changes to Cloudflare's API rate limitations.
+
+You should instead look at the [Cloudflare Workers Bouncer](/u/bouncers/cloudflare).
+
+:::
+
+A Remediation Component that syncs the decisions made by CrowdSec with CloudFlare's firewall. Manages multi user, multi account, multi zone setup. Supports IP, Country and AS scoped decisions.
+
+
+
+## Installation
+
+### Repository
+
+Packages for crowdsec-cloudflare-bouncer [are available on our repositories](/u/getting_started/installation/linux#repository-installation). You need to pick the package accord to your firewall system :
+
+
+
+
+```bash
+sudo apt install crowdsec-cloudflare-bouncer
+```
+
+
+
+
+```bash
+sudo yum install crowdsec-cloudflare-bouncer
+```
+
+
+
+
+
+Then run the following commands to setup your bouncer:
+
+
+```bash
+sudo crowdsec-cloudflare-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml # auto-generate cloudflare config for provided space separated tokens
+sudo crowdsec-cloudflare-bouncer -s # this sets up IP lists and firewall rules at cloudflare for the provided config.
+sudo systemctl start crowdsec-cloudflare-bouncer # the bouncer now syncs the crowdsec decisions with cloudflare components.
+```
+
+:::warning
+
+Please configure your server to emit real IPs rather than cloudflare IPs in logs, so crowdsec can function properly. See how to [here](https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs)
+
+:::
+
+:::info
+
+If your component is not installed on the same machine than LAPI, don't forget to set the `crowdsec_lapi_url` and `crowdsec_lapi_key` in the configuration file `/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml`
+
+:::
+
+:::note
+
+You need to run `sudo crowdsec-cloudflare-bouncer -d` to cleanup exisiting cloudflare components created by component before editing the config files.
+
+:::
+
+:::note
+
+You can run `sudo crowdsec-cloudflare-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml` to generate the configuration by discovering all the accounts and the zones associated with the provided tokens.
+
+:::
+
+
+### Manual
+
+#### Assisted
+
+Download the [latest release](https://github.com/crowdsecurity/cs-cloudflare-bouncer/releases).
+
+```bash
+tar xzvf crowdsec-cloudflare-bouncer.tgz
+cd crowdsec-cloudflare-bouncer/
+sudo ./install.sh
+sudo crowdsec-cloudflare-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml # auto-generate cloudflare config for provided tokens
+sudo crowdsec-cloudflare-bouncer -s # this sets up IP lists and firewall rules at cloudflare for the provided config.
+sudo systemctl start crowdsec-cloudflare-bouncer # the bouncer now syncs the crowdsec decisions with cloudflare components.
+```
+
+#### From source
+
+```bash
+make release
+cd crowdsec-cloudflare-bouncer-vX.X.X
+sudo ./install.sh
+```
+Rest of the steps are same as of the above method.
+
+
+## Container
+
+Make sure you have docker or podman installed. In this guide we will use docker, but podman would work as a drop in replacement too.
+
+### Setup
+
+```bash
+docker run crowdsecurity/cloudflare-bouncer \
+ -g , > cfg.yaml # auto-generate cloudflare config for provided space separated tokens
+```
+
+You can then review the contents of the file `cfg.yaml` and make any necessary changes.
+
+```
+vim cfg.yaml # review config and set `crowdsec_lapi_key`
+```
+
+The `crowdsec_lapi_key` can be obtained by running the following:
+
+```bash
+sudo cscli -oraw bouncers add cloudflarebouncer # -oraw flag can discarded for human friendly output.
+```
+
+The `crowdsec_lapi_url` must be accessible from the container.
+
+### Runtime
+
+```bash
+ docker run \
+ -v $PWD/cfg.yaml:/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml \
+ -p 2112:2112 \
+ crowdsecurity/cloudflare-bouncer
+```
+
+
+## Configuration
+
+Configuration file can be found at `/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml`
+
+```yaml
+# CrowdSec Config
+crowdsec_lapi_url: http://localhost:8080/
+crowdsec_lapi_key: ${API_KEY}
+crowdsec_update_frequency: 10s
+include_scenarios_containing: [] # ignore IPs banned for triggering scenarios not containing either of provided word, eg ["ssh", "http"]
+exclude_scenarios_containing: [] # ignore IPs banned for triggering scenarios containing either of provided word
+only_include_decisions_from: [] # only include IPs banned due to decisions orginating from provided sources. eg value ["cscli", "crowdsec"]
+
+#Cloudflare Config.
+cloudflare_config:
+ accounts:
+ - id:
+ token:
+ ip_list_prefix: crowdsec
+ default_action: managed_challenge
+ total_ip_list_capacity: # only this many latest ip scoped decisions would be kept
+
+ zones:
+ - actions:
+ - managed_challenge # valid choices are either of managed_challenge, js_challenge, block
+ zone_id:
+
+ update_frequency: 30s # the frequency to update the cloudflare IP list
+
+# Component Config
+daemon: true
+log_mode: file
+log_dir: /var/log/
+log_level: info # valid choices are either debug, info, error
+log_max_size: 40
+log_max_age: 30
+log_max_backups: 3
+compress_logs: true
+
+prometheus:
+ enabled: true
+ listen_addr: 127.0.0.1
+ listen_port: 2112
+```
+
+## Making changes to configuration
+
+The component creates Cloudflare infra (IP lists, rules etc) according to your config file.
+
+Before changing the config, always run the following command to clear old infra:
+
+```
+sudo crowdsec-cloudflare-bouncer -d
+```
+
+### Upgrading from v0.0.X to v0.1.Y
+
+During v0.0.X there was no `managed_challenge` action, instead `challenge` action was used by bouncer. This is deprecated since v0.1.0 .
+
+This section assumes you used the default config (generated via `crowdsec-cloudflare-bouncer -g ,` )
+
+After upgrading the component from v0.0.X to v0.1.Y , run the following commands to migrate to `managed_challenge`.
+
+```bash
+sudo crowdsec-cloudflare-bouncer -d
+sudo crowdsec-cloudflare-bouncer -g , -o
+sudo systemctl restart crowdsec-cloudflare-bouncer
+```
+
+
+## Cloudflare Configuration
+
+**Background:** In Cloudflare, each user can have access to multiple accounts. Each account can own/access multiple zones. In this context a zone can be considered as a domain. Each domain registered with cloudflare gets a distinct `zone_id`.
+
+
+For obtaining the `token`:
+1. Sign in as a user who has access to the desired account.
+2. Go to [Tokens](https://dash.cloudflare.com/profile/api-tokens) and create the token. The component requires the following permissions to function.
+
+
+To automatically generate config for cloudflare check the helper section below.
+
+
+:::note
+If the zone is subscribed to a paid Cloudflare plan then it can be configured to support multiple types of actions. For free plan zones only one action is supported. The first action is applied as default action.
+:::
+
+## Helpers
+
+The component binary has built in helper scripts to do various operations.
+
+### Auto config generator
+
+Generates component config by discovering all the accounts and the zones associated with provided list of tokens.
+
+Example Usage:
+
+```bash
+sudo crowdsec-cloudflare-bouncer -g ,... -o /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml
+```
+
+:::note
+This script only generates cloudflare related config. By default it refers to the config at `/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml` for crowdsec configuration.
+:::
+
+Using custom config:
+
+```bash
+sudo crowdsec-cloudflare-bouncer -c /path/to/config/file -g ,...
+```
+
+### Cloudflare Setup
+
+This only creates the required IP lists and firewall rules at cloudflare and exits.
+
+Example Usage:
+```bash
+sudo crowdsec-cloudflare-bouncer -s
+```
+
+### Cloudflare Cleanup
+
+This deletes all IP lists and firewall rules at cloudflare which were created by the component.
+
+Example Usage:
+```bash
+sudo crowdsec-cloudflare-bouncer -d
+```
+
+## How it works
+
+The service polls the CrowdSec Local API for new / deleted decisions. It then makes API calls to Cloudflare to update IP lists and firewall rules depending upon the decision.
+
+## Configuration Reference
+
+### `crowdsec_lapi_url`
+> string
+
+The URL of CrowdSec LAPI. It should be accessible from the component.
+
+### `crowdsec_lapi_key`
+> string
+
+API key to authenticate with the LAPI.
+
+### `cert_path`
+> string
+
+Path to the certificate file used to authenticate with the LAPI.
+
+### `key_path`
+> string
+
+Path to the key file used to authenticate with the LAPI.
+
+### `ca_path_file`
+> string
+
+Path to the CA file used to trust the LAPI certificate.
+
+### `crowdsec_update_frequency`
+> string (That is parseable by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration))
+
+The component will poll the CrowdSec every `update_frequency` interval.
+
+### `include_scenarios_containing`
+> [ ]string
+
+Ignore IPs banned for triggering scenarios not containing either of provided word.
+
+```yaml title="Example"
+include_scenarios_containing: ["ssh", "http"]
+```
+
+### `exclude_scenarios_containing`
+> [ ]string
+
+Ignore IPs banned for triggering scenarios containing either of provided word.
+
+```yaml title="Example"
+exclude_scenarios_containing: ["ssh", "http"]
+```
+
+### `only_include_decisions_from`
+> [ ]string
+
+Only include IPs banned due to decisions orginating from provided sources.
+
+```yaml title="Example"
+only_include_decisions_from: ["cscli", "crowdsec"]
+```
+
+### `cloudflare_config`
+> [CloudflareConfig](https://github.com/crowdsecurity/cs-cloudflare-bouncer/blob/20c902ee1e95fe13135dd493d7e96840bafc931b/pkg/cfg/config.go#L34-L37)
+
+This block contains cloudflare specific config.
+
+#### `update_frequency`
+> string (That is parseable by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration))
+
+The frequency at which to update the cloudflare resources.
+
+```yaml title="Example"
+update_frequency: "10s"
+```
+
+#### `accounts`
+> [ ][AccountConfig](https://github.com/crowdsecurity/cs-cloudflare-bouncer/blob/20c902ee1e95fe13135dd493d7e96840bafc931b/pkg/cfg/config.go#L26-L33)
+
+List of account of configs
+
+##### `id`
+> string
+
+id of cloudflare account
+
+##### `token`
+> string
+
+cloudflare token to use to access the account.
+
+##### `ip_list_prefix`
+> string
+
+The prefix to use for naming the IP lists created by the bouncer. The name of IP list will be of the form `ip_list_prefix`+`action`.
+
+##### `total_ip_list_capacity`
+> int
+
+Limit the number of items in IP lists. This is required for avoiding limit of 10k items for lists.
+
+##### `default_action`
+> `managed_challenge` | `block` | `js_challenge` | `challenge` | `none`
+
+The action to be applied for a decision, if the decision's action is not supported by a zone.
+
+`default_action` must be supported by all zones.
+
+**Example:**
+
+Consider your zone config supports the actions `managed_challenge` and `js_challenge`. Your `default_action` is `managed_action`. If you create the following decision:
+
+```
+sudo cscli decisions add --ip 192.168.1.1 --type ban
+```
+
+Since the zone doesn't support `ban` decision type, it'll be inserted into the IP list given by `default_action`. In this case it'll be the list for `managed_challenge`.
+
+You can completely ignore such decisions by setting `default_action` to `none`. It won't be inserted into any list then.
+
+**Note:**
+
+Following table is mapping of decision type to it's destination IP list.
+
+| Decision Type | Default Action |
+| ------------- | ----------------- |
+| captcha | managed_challenge |
+| ban | block |
+| js_challenge | js_challenge |
+
+
+:::warning
+`challenge` action is deprecated in favour of `managed_challenge`.
+:::
+
+#### `zones`
+> [ ][ZoneConfig](https://github.com/crowdsecurity/cs-cloudflare-bouncer/blob/20c902ee1e95fe13135dd493d7e96840bafc931b/pkg/cfg/config.go#L21-L25)
+
+This block contains config for each zone to be managed by the component. The zone must be accessible from the parent account.
+
+##### `zone_id`
+> string
+
+The id of the zone.
+
+##### `actions`
+> [ ]string
+
+List of actions to be supported by this zone. If the zone is not subscribed to premium plan, then only a single action can be given.
+
+The supported action must include the `default_action` of the parent account.
+
+Valid choice includes either of
+- `block`
+- `js_challenge`
+- `challenge`
+- `managed_challenge`.
+
+The component creates an IP list for each action. IP list is at account level, so multiple zones with same parent account will share lists for particular action.
+
+:::warning
+`challenge` action is deprecated in favour of `managed_challenge`
+:::
+
+**Note:**
+
+Following table is mapping of decision type to it's destination IP list, which are created according to zone actions
+
+
+| Decision Type | Zone Action |
+| ------------- | ----------------- |
+| captcha | managed_challenge |
+| ban | block |
+| js_challenge | js_challenge |
+
+
+
+### `daemon`
+> boolean
+
+:::warning
+This field has now been deprecated and is ignored within the component
+:::
+
+Run the component as a daemon.
+
+### `log_mode`
+> `stdout` | `file`
+
+Where the log contents are written (With `file` it will be written to `log_dir` with the name `crowdsec-cloudflare-bouncer.log`)
+
+### `log_dir`
+> string
+
+Relevant if `log_mode` is `file`. This determines where to create log file.
+
+### `log_level`
+> `trace` | `debug` | `info` | `error`
+
+Log level for the component.
+
+### `compress_logs`
+> `true` | `false`
+
+Compress log files on rotation
+
+### `log_max_size`
+> int (in MB)
+
+Max size of log files before rotation
+
+### `log_max_backups`
+> int
+
+How many backup log files to keep before deletion (can happen before `log_max_age` is reached)
+
+### `log_max_age`
+> int (in days)
+
+Max age of backup files before deletion (can happen before `log_max_backups` is reached)
+
+## Troubleshooting
+ - Metrics can be seen at http://localhost:2112/metrics
+ - Logs are in `/var/log/crowdsec-cloudflare-bouncer.log` (Default unless changed in config)
+ - You can view/interact directly in the ban list either with `cscli`
+ - Service can be started/stopped with `systemctl start/stop crowdsec-cloudflare-bouncer`
diff --git a/crowdsec-docs/unversioned/bouncers/cloudflare-worker-installer.mdx b/crowdsec-docs/unversioned/bouncers/cloudflare-worker-installer.mdx
deleted file mode 100644
index a031d99d2..000000000
--- a/crowdsec-docs/unversioned/bouncers/cloudflare-worker-installer.mdx
+++ /dev/null
@@ -1,129 +0,0 @@
----
-id: cloudflare-worker-installer
-title: Cloudflare Worker Bouncer — Web Installer
----
-
-import useBaseUrl from '@docusaurus/useBaseUrl';
-import RemediationSupportBadges from '@site/src/components/remediation-support-badge';
-
-
-
-
-
-
-
-
-
-
-
-
-:::tip Recommended for Autonomous Mode
-Fully self hosted on Cloudflare it's the easiest way to deploy CrowdSec Blocklists on Cloudflare
-(no persistent daemon, plugged directly to a [Blocklist as a Service integration](/u/integrations/remediationcomponent)), this installer is the easiest way to get started.
-
-For **Daemon Mode** (Security Engine + Go process), use the [CLI-based installation](/u/bouncers/cloudflare-workers#installation) instead.
-:::
-
-## Overview
-
-The **CrowdSec Cloudflare Worker Bouncer Installer** is a web-based GUI that lets you deploy and manage the [Cloudflare Worker Bouncer](/u/bouncers/cloudflare-workers) in **Autonomous Mode** — directly from your browser, with no command-line required.
-
-It runs as a Cloudflare Worker itself and communicates with the Cloudflare API on your behalf to:
-
-- Verify your API token and auto-load your accounts and zones
-- Set up (or update) the CrowdSec BLaaS integration endpoint
-- Install or remove the bouncer on a per-zone basis
-- Configure Turnstile (CAPTCHA) challenges per zone
-
-## How it Works
-
-The installer is itself a Cloudflare Worker application (built with React Router + Hono). Once deployed to your Cloudflare account, it provides a step-by-step UI:
-
-1. **Cloudflare API Token** — paste your token; the installer validates it and confirms permissions automatically.
-2. **CrowdSec Integration Endpoint** — paste your BLaaS (Blocklist as a Service) URL; the installer reads and displays the currently configured endpoint.
-3. **Zone Protection** — browse your zones, see which are protected and which are not, and install or remove the bouncer per zone with one click.
-
-No YAML config files, no CLI flags.
-
-### Deployed Resources
-
-When you install the bouncer on a zone, the installer creates the following resources in your Cloudflare account:
-
-| Resource | Name | Purpose |
-|---|---|---|
-| KV Namespace | `CROWDSECCFBOUNCERNS` | Stores decisions and configuration |
-| Main Worker | `crowdsec-cloudflare-worker-bouncer` | Applies decisions to incoming requests |
-| Sync Worker | `crowdsec-decisions-sync-worker` | Periodically fetches decisions from CrowdSec BLaaS |
-| Cron Trigger | Every 5 min (default) | Schedules the sync worker |
-| Worker Routes | Per zone | Routes traffic through the bouncer |
-| Turnstile Widgets | Per zone | Powers CAPTCHA challenges |
-
-## Prerequisites
-
-- A Cloudflare account with at least one zone
-- A **Cloudflare API Token** with the [required permissions](/u/bouncers/cloudflare-workers#generating-a-cloudflare-api-token)
-- A **CrowdSec BLaaS Integration Endpoint** — set up via the [CrowdSec Console](/u/integrations/remediationcomponent)
-
-## Deployment
-
-The installer is deployed with a single click via the Cloudflare deploy button:
-
-[](https://deploy.workers.cloudflare.com/?url=https://github.com/crowdsecurity/cs-cloudflare-worker-bouncer-install)
-
-This deploys the installer Worker to your Cloudflare account. Once deployed, open the Worker's URL in your browser to access the UI.
-
-:::info
-The installer Worker itself does not store your API token or credentials. Your token is used only within the current browser session and is never persisted.
-:::
-
-## Step-by-Step Guide
-
-### Step 1 — Cloudflare API Token
-
-Paste your Cloudflare API token in the first section. The installer verifies it immediately and confirms that all required permissions are in place.
-
-If you don't have a token yet, click **Create token ↗** to open the Cloudflare dashboard with the correct permissions pre-selected. See [Generating a Cloudflare API Token](/u/bouncers/cloudflare-workers#generating-a-cloudflare-api-token) for the full list of required permissions.
-
-### Step 2 — CrowdSec Integration Endpoint
-
-Paste your **BLaaS Integration Endpoint URL** from the [CrowdSec Console](https://app.crowdsec.net). The installer reads and displays the currently configured endpoint so you can verify it before proceeding.
-
-:::note
-The BLaaS endpoint is the URL of a Remediation Component integration created in the CrowdSec Console. It is used by the sync worker to fetch the list of IPs to block.
-See [Blocklist as a Service integration](/u/integrations/remediationcomponent) for setup instructions.
-:::
-
-### Step 3 — Zone Protection
-
-The installer lists all zones in your Cloudflare account and shows their current status:
-
-- **Protected** — the bouncer is installed and active on this zone
-- **Unprotected** — the bouncer is not installed on this zone
-
-Select one or more zones and click **Install** to deploy, or **Remove** to uninstall from that zone. You can manage zones individually without affecting the others.
-
-#### Captcha (Turnstile)
-
-When installing a zone, you can enable **Turnstile** to serve a CAPTCHA challenge instead of a hard block. The installer creates a Turnstile widget for each protected zone automatically.
-
-You can toggle Turnstile per zone from the Zone Protection section after installation.
-
-## Updating the BLaaS Endpoint
-
-If you change your CrowdSec integration endpoint, you can update it without reinstalling the bouncer. Simply paste the new endpoint in **Step 2** and click **Edit** — the installer updates the sync worker's credentials in the KV store, and the next sync cycle will use the new endpoint.
-
-## Removing the Bouncer
-
-To remove the bouncer from a zone, select the zone in **Step 3** and click **Remove**. This deletes the worker routes and Turnstile widget for that zone only — the shared KV namespace and worker scripts remain in place for other protected zones.
-
-To remove all bouncer infrastructure at once (KV, workers, all routes, all Turnstile widgets), click **Uninstall all** in the Zone Protection section.
-
-## Relationship with the Cloudflare Worker Bouncer
-
-The installer deploys the same workers as the [CLI-based Cloudflare Worker Bouncer](/u/bouncers/cloudflare-workers) in **Autonomous Mode**. The two methods are interchangeable — you can install with the GUI and manage with the CLI, or vice versa, as long as you use the same resource names (`CROWDSECCFBOUNCERNS`, `crowdsec-cloudflare-worker-bouncer`, `crowdsec-decisions-sync-worker`).
-
-For advanced configuration (custom KV namespace names, multiple bouncer instances on the same account, daemon mode, Prometheus metrics), refer to the full [Cloudflare Worker Bouncer documentation](/u/bouncers/cloudflare-workers).
-
-## Source
-
-The installer source code is available at [crowdsecurity/cs-cloudflare-worker-bouncer-install](https://github.com/crowdsecurity/cs-cloudflare-worker-bouncer-install).
diff --git a/crowdsec-docs/unversioned/bouncers/cloudflare-workers.mdx b/crowdsec-docs/unversioned/bouncers/cloudflare-workers.mdx
deleted file mode 100644
index 40dfb88a2..000000000
--- a/crowdsec-docs/unversioned/bouncers/cloudflare-workers.mdx
+++ /dev/null
@@ -1,841 +0,0 @@
----
-id: cloudflare-workers
-title: CrowdSec Cloudflare Worker
-toc_max_heading_level: 3
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import useBaseUrl from '@docusaurus/useBaseUrl';
-import RemediationSupportBadges from '@site/src/components/remediation-support-badge';
-
-
-
-
-
-
-
-
-
-
-
-
-
-📚 Documentation
-💠 Hub
-💬 Discourse
-
-
-
-
-## Overview
-This **Remediation Component** (aka Bouncer) uses **Cloudflare Workers** to `block` or `challenge` incoming requests.
-- It uses decisions taken by your **CrowdSec Security Engine** and/or **Blocklists** you subscribed to.
-- It can be directly plugged to **CrowdSec Blocklists** via a CrowdSec **Blocklist as a Service Integration**.
-
-The bouncer uses **Cloudflare Workers** in combination with **Cloudflare Workers KV** (KeyValue store).
-
-#### Useful links
-
-* **Register** this **remediation component** into your **Security engine**: [Here](/u/bouncers/intro).
-* OR setup a **Blocklist as a Service** endpoint for a **remediation component**: [Here](/u/integrations/remediationcomponent).
-
-:::warning
-This Remediation Component heavily relies on Cloudflare Workers and KV store.
-It works best on a paid Workers subscription.
-More explanation in the chapter [Test with Cloudflare free plan](#appendix-test-with-cloudflare-free-plan)
-:::
-
-## Operational modes
-
-This Remediation Component can operate in two modes:
- - [**Daemon Mode**](#daemon-mode): A Go process runs continuously on a host/vm, periodically syncing decisions from your CrowdSec Security Engine to Cloudflare.
- - [**Autonomous Mode**](#autonomous-mode): A Go process creates/delete an autonomous Cloudflare Worker that periodically syncs decisions within Cloudflare itself.
-
-### Daemon Mode
-
-This mode is recommended if you are using a **Security Engine** and want to relay all/part of the decisions (and subscribed blocklists) to Cloudflare.
-It provides more **control** and **flexibility** over the decision synchronization process.
-It requires the Bouncer (Go process) to run continuously on a host/VM, usually alongside your CrowdSec Security Engine.
-
-:::info
-The Workers and KV created by the bouncer will be cleaned up from Cloudflare upon launch error OR bouncer stoppage.
-:::
-
-
-
-### Autonomous Mode
-
-This mode is recommended if you want a **minimal footprint** deployment, without having the bouncer process running on your host/VM.
-💡 It is ideal if you want to directly plug a **Blocklist integrations** into Cloudflare.
-
-*It can also be plugged to a Security Engine, but woould require an open connection from Cloudflare to your LAPI. We recommend using [Daemon mode](#daemon-mode) in a usecase where a Security Engine is involved.*
-
-:::tip Web Installer available
-Looking for a no-CLI setup? The **[Cloudflare Worker Bouncer Installer](/u/bouncers/cloudflare-worker-installer)** is a browser-based GUI that deploys the bouncer in Autonomous Mode with a step-by-step wizard — no YAML or command line required.
-:::
-
-#### ⚙️ Init / Removal on CloudFlare
-
-Creation of necessary **Cloudflare Workers** and **KV stores** is done once at init time.
-And can be cleaned up by running the bouncer with the appropriate instruction* (more details int the installation section).*
-
-The init phase create two workers and one KV store.:
- - The **remediation worker**, which applies the decisions to incoming requests:
- - It uses the **KV store** to lookup decisions.
- - It is bound to **worker routes** to protect your selected zones.
- - The **decisions sync worker**, which periodically fetches and caches decisions and updates the **KV store**.
-
-The cleanup phase deletes all the created components.
-
-
-
-#### ⚙️ Periodic decision sync within Cloudflare
-
-Once initialized, the decisions sync worker runs periodically based on a cron schedule defined in the configuration.
-
-
-
-## Installation
-
-:::warning
-After configuring and starting the Remediation Component, please see the [setting up worker fail mode](#setting-up-the-worker-route-fail-mode) section.
-:::
-
-### Prerequisites
-
-Before installing the CrowdSec Cloudflare Worker Remediation Component, you need a **Cloudflare API token** with the required permissions to create and manage Workers, KV stores, and other Cloudflare resources
-
-For instructions on generating a Cloudflare API token with the correct permissions, see [Generating a Cloudflare API Token](#generating-a-cloudflare-api-token).
-
-### Packages Installation
-
-Packages for crowdsec-cloudflare-worker-bouncer [are available on our repositories](/u/getting_started/installation/linux#repository-installation). You need to pick the package accord to your firewall system :
-
-
-
-
-```bash
-sudo apt install crowdsec-cloudflare-worker-bouncer
-```
-
-
-
-
-```bash
-sudo yum install crowdsec-cloudflare-worker-bouncer
-```
-
-
-
-
-
-Then run the following commands to setup your Remediation Component:
-
-
-
-
-```bash
-sudo crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # auto-generate cloudflare config for provided space separated tokens
-sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # review config and set `crowdsec.lapi_key` if haven't alread
-sudo systemctl start crowdsec-cloudflare-worker-bouncer # the Remediation Component now syncs the crowdsec decisions with cloudflare components.
-```
-
-
-
-
-```bash
-sudo crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # auto-generate cloudflare config for provided space separated tokens
-sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # review config and set `crowdsec.lapi_key` if haven't alread
-sudo crowdsec-cloudflare-worker-bouncer -S -c /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # deploy in autonomous mode - no daemon needed
-```
-
-In autonomous mode, the Go process deploys the configuration to Cloudflare and exits. All decision synchronization is handled by Cloudflare scheduled workers. No systemd service is required.
-
-
-
-
-:::warning
-
-Please configure your server to emit real IPs rather than cloudflare IPs in logs, so crowdsec can function properly. See how to [here](https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs)
-
-:::
-
-:::info
-
-If your Remediation Component is not installed on the same machine than LAPI, don't forget to set the `crowdsec_lapi_url` and `crowdsec.lapi_key` in the configuration file `/etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml`
-
-:::
-
-:::note
-
-You need to run `sudo crowdsec-cloudflare-worker-bouncer -d` to cleanup existing cloudflare components created by Remediation Component before editing the config files.
-
-:::
-
-:::note
-
-You can run `sudo crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml` to generate the configuration by discovering all the accounts and the zones associated with the provided tokens.
-
-:::
-
-### Manual Installation
-
-#### Assisted
-
-Download the [latest release](https://github.com/crowdsecurity/cs-cloudflare-worker-bouncer/releases).
-
-
-
-
-```bash
-tar xzvf crowdsec-cloudflare-worker-bouncer.tgz
-cd crowdsec-cloudflare-worker-bouncer/
-sudo ./install.sh
-sudo crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # auto-generate cloudflare config for provided tokens
-sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # review config and set `crowdsec.lapi_key` if haven't already
-sudo systemctl start crowdsec-cloudflare-worker-bouncer # the Remediation Component now syncs the crowdsec decisions with cloudflare components.
-```
-
-
-
-
-```bash
-tar xzvf crowdsec-cloudflare-worker-bouncer.tgz
-cd crowdsec-cloudflare-worker-bouncer/
-sudo ./install.sh
-sudo crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # auto-generate cloudflare config for provided tokens
-sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # review config and set `crowdsec.lapi_key` if haven't already
-sudo crowdsec-cloudflare-worker-bouncer -S -c /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # deploy in autonomous mode - no daemon needed
-```
-
-
-
-
-#### From source
-
-:warning: requires go >= 1.23
-
-
-
-
-```bash
-git clone https://github.com/crowdsecurity/cs-cloudflare-worker-bouncer
-cd cs-cloudflare-worker-bouncer
-make release
-cd crowdsec-cloudflare-worker-bouncer-*
-./crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # auto-generate cloudflare config for provided space separated tokens
-sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # review config and set `crowdsec.lapi_key` if haven't already
-sudo systemctl start crowdsec-cloudflare-worker-bouncer
-```
-
-
-
-
-```bash
-git clone https://github.com/crowdsecurity/cs-cloudflare-worker-bouncer
-cd cs-cloudflare-worker-bouncer
-make release
-cd crowdsec-cloudflare-worker-bouncer-*
-./crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # auto-generate cloudflare config for provided space separated tokens
-sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # review config and set `crowdsec.lapi_key` if haven't already
-./crowdsec-cloudflare-worker-bouncer -S -c /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml # deploy in autonomous mode - no daemon needed
-```
-
-
-
-
-
-## How it works
-
-The Remediation Component can operate in two modes:
-
-### Daemon Mode (default)
-
-The Remediation Component does the following:
-
-1. Create a Cloudflare Worker and a Worker KV per configured account.
-2. Create a Worker Route(s) per configured zone. Any request matching the route would be handled by the worker.
-3. For every matching incoming request, the worker checks whether it's IP, Country and AS have a decision against. It checks for this in it's KV store. If found it performs the corresponding remediation.
-4. The Remediation Component runs as a daemon and periodically updates the KV store with the latest decisions from CrowdSec's **Security Engine** OR **Blocklist Integration**.
-
-*Note that in the following Schema, a Blocklist as a Service Integration can be substituted to the Security Engine.*
-
-
-
-
-
-### Autonomous Mode
-
-:::info
-Autonomous mode is ideal for users who prefer not to run a continuous process on their host/VM.
-Combined with [Blocklist as a Service (BLaaS)](/u/integrations/remediationcomponent), this provides the minimal footprint deployment, only running setup/cleanup commands when needed.
-:::
-
-In autonomous mode (enabled with the `-S` flag), the Remediation Component functions without requiring a continuously running Go daemon process. Instead:
-
-1. Two Cloudflare Workers are deployed:
- - `crowdsec-cloudflare-worker-bouncer` (Remediation Worker) - applies cached decisions to incoming requests (same as daemon mode)
- - `decisions-sync-worker` (Blocklist updater Worker) - periodically fetches and caches security decisions using Cloudflare scheduled tasks
-
-
-
-2. The Go process only needs to run once for initial setup and configuration deployment
-
-3. All decision synchronization is handled automatically by the Blocklist updater Worker using Cloudflare's scheduled tasks
-
-
-
-4. This mode reduces infrastructure requirements as no persistent daemon is needed
-
-
-
-
-#### Reset all decisions from Cloudflare KV store
-
-In autonomous mode, you can reset all decisions without redeploying all the infrastructure (with the `-S` flag).
-In order to do so, add a `RESET` key (with a `true` value) to the KV store.
-The next time the sync worker runs, it will clear all existing decisions and repopulate them from CrowdSec LAPI.
-
-
-### Metrics
-
-The worker writes metric data points to a [Workers Analytics Engine](https://developers.cloudflare.com/analytics/analytics-engine/) dataset, tracking:
- - Number of requests processed
- - Number of requests blocked
- - Number of requests that threw an exception in the worker
- - Average request processing latency
-
-:::info
-Since `v0.0.18`, metrics are stored in a **Workers Analytics Engine** dataset instead of a **D1** database. Make sure your Cloudflare token has the `Account Analytics: Read` permission (see [the permissions table](#generating-a-cloudflare-api-token)). Without it the metric poll returns a `403` and metrics are disabled, but remediation enforcement is unaffected.
-
-If you are upgrading from an older version, the legacy `CROWDSECCFBOUNCERDB` D1 database is no longer used and can be deleted manually from the Cloudflare dashboard.
-:::
-
-The dataset name defaults to `crowdsec_cloudflare_bouncer` and can be customized with the [`worker.analytics_dataset`](#workeranalytics_dataset) option.
-
-**In Daemon Mode**, the running process:
-- Polls the Analytics Engine SQL API on the regular push schedule
-- Exposes metrics through the Prometheus endpoint
-- Automatically pushes metrics to CrowdSec for visualization with `cscli`
-
-**In Autonomous Mode**, since there is no running daemon process:
-- Metrics are collected in the Analytics Engine dataset
-- Metrics are **NOT pushed to CrowdSec**
-
-## Configuration explained
-
-```yaml
-crowdsec_config:
- lapi_key: ${API_KEY}
- lapi_url: ${CROWDSEC_LAPI_URL}
- update_frequency: 10s
- include_scenarios_containing: []
- exclude_scenarios_containing: []
- only_include_decisions_from: []
- insecure_skip_verify: false
- key_path: "" # Used for TLS authentification with CrowdSec LAPI
- cert_path: "" # Used for TLS authentification with CrowdSec LAPI
- ca_cert_path: "" # Used for TLS authentification with CrowdSec LAPI
-
-cloudflare_config:
- accounts:
- - id:
- zones:
- - zone_id: # crowdflare.co.uk
- actions: # Supported Actions [captcha, ban]
- - captcha
- default_action: captcha # Supported Actions [captcha, ban, none]
- routes_to_protect: []
- turnstile:
- enabled: true
- rotate_secret_key: true
- rotate_secret_key_every: 168h0m0s
- mode: managed # Supported Modes "managed"|"invisible"|"non-interactive"
- token:
- account_name: owner@example.com
- worker:
- log_only: false # If true, allow all requests, but still keep track of what would have been blocked in the metrics
- script_name: ""
- kv_namespace_name: "" # KV namespace title; change when running multiple bouncers on the same Cloudflare account
- decisions_sync_script_name: "" # Decisions sync worker script name; change when running multiple bouncers on the same Cloudflare account
- analytics_dataset: "" # Workers Analytics Engine dataset name for metrics (default: crowdsec_cloudflare_bouncer)
- logpush: null
- tags: []
- compatibility_date: ""
- compatibility_flags: []
- observability: # Optional Workers Observability (logs/traces); omit to leave Cloudflare defaults in place
- enabled: true
- head_sampling_rate: 1.0
- traces:
- enabled: true
- head_sampling_rate: 1.0
- decisions_sync_worker: # Configuration for autonomous decisions sync worker
- cron: '*/5 * * * *' # Cron schedule for syncing decisions (e.g., "*/5 * * * *" for every 5 minutes)
-
-log_level: info
-log_media: "stdout"
-log_dir: "/var/log/"
-ban_template_path: "" # set to empty to use default template
-
-prometheus:
- enabled: true
- listen_addr: 127.0.0.1
- listen_port: "2112"
-```
-
-
-## Cloudflare Configuration
-
-**Background:** In Cloudflare, each user can have access to multiple accounts. Each account can own/access multiple zones. In this context a zone can be considered as a domain. Each domain registered with cloudflare gets a distinct `zone_id`.
-
-### Generating a Cloudflare API Token
-
-For obtaining the `token`:
-
-:::warning
-Ensure the `token` you are generating is a **user** API token these are found via clicking "my profile" and `API Tokens`. If you are in the `Account API Tokens` section then this is the incorrect place and these tokens have different rate limits which may cause issues whilst operating this Remediation Component.
-:::
-
-1. Sign in as a user who has access to the desired account.
-
-Then click [**this link**](https://dash.cloudflare.com/profile/api-tokens?permissionGroupKeys=%5B%7B%22key%22%3A%22account_settings%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22challenge_widgets%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22user_details%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22workers_kv_storage%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22workers_routes%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22workers_scripts%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22zone%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22dns%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22account_analytics%22%2C%22type%22%3A%22read%22%7D%5D&name=) and create the token.
-
-Alternatively, you can go to [Tokens](https://dash.cloudflare.com/profile/api-tokens) and create the token.
-
-The Remediation Component requires the following permissions to function:
-
-| Permission Group | Item | Permission |
-| ---------------- | ------------------ | ---------- |
-| Account | Turnstile | Edit |
-| Account | Workers KV Storage | Edit |
-| Account | Workers Scripts | Edit |
-| Account | Account Settings | Read |
-| Account | Account Analytics | Read |
-| User | User Details | Read |
-| Zone | DNS | Read |
-| Zone | Workers Routes | Edit |
-| Zone | Zone | Read |
-
-
-:::info
-By default, the token will be scopped to all accounts / zones you have access to.\
-We recommend scoping the token only to the required accounts and zones.
-:::
-
-:::warning
-
-This remediation component require each configured to have at least one `A` or `AAAA` records.\
-If you have zones without them (eg, with only `CNAME` records), exclude them from the scope.\
-The bouncer will try to automatically ignore such zones, but if it fails to do so for any reason, you might incur higher KV storage charges.
-:::
-
-To automatically generate config, check the helper section below.
-
-## Helpers
-
-The binary has built in helper scripts to do various operations.
-
-### Auto config generator
-
-Generates config by discovering all the accounts and the zones associated with provided list of tokens.
-
-Example Usage:
-
-```bash
-sudo crowdsec-cloudflare-worker-bouncer -g ,... -o cfg.yaml
-cat cfg.yaml > /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
-```
-
-:::note
-This script only generates cloudflare related config. By default it refers to the config at `/etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml` for crowdsec configuration.
-:::
-
-Using custom config:
-```bash
-sudo crowdsec-cloudflare-worker-bouncer -c ./cfg.yaml -g ,...
-```
-
-### Cloudflare Cleanup
-
-This deletes all the Cloudflare infrastructure which was created by the Remediation Component.
-
-Example Usage:
-```bash
-sudo crowdsec-cloudflare-worker-bouncer -d
-```
-
-## Setting up the worker route fail mode
-
-The Remediation Component creates worker routes to make the workers act as a reverse proxy for your origin servers. The worker routes are created with the failover mode set to `Fail Closed`. **There's no public Cloudflare API we can use to change/update it to `Fail Open` mode**.
-
-With `Fail Closed` mode, Routes in fail closed mode will display a Cloudflare 1027 error page to visitors if there's an error within the worker. This error could be triggered due to quotas exceeding your plan etc. **Cloudflare doesn't mention all the possible scenarios which could trigger this error page**.
-
-Thus **we recommend you to manually override the failover mode to `Fail Open`** for all the worker routes created by our Remediation Component. With `Fail Open` mode the requests would bypass the worker and be served directly from your origin servers. Thus your website would continue to function even if there's an error within the worker.
-
-This can be done by following the steps below:
-
-1. Log in to the Cloudflare dashboard and select your account.
-2. For all the websites configured with the Remediation Component, do the following:
-3. Click on the website's name to open the Website's Overview page.
-4. Click on the Worker Routes tab from the left menu.
-
-
-
-5. Click on the route created by the Remediation Component.
-6. Click on the Edit button.
-7. Click on the Request limit failure mode. Check the Fail open button.
-
-
-
-## Appendix: Test with Cloudflare free plan
-Using Cloudflare's free plan with our Remediation Component requires to understand the constraints of the CloudFlare freeplan.
-Despite these constraints, it's entirely feasible to leverage the Remediation Component for enhanced security.
-
-In this section we'll guide you through:
- - An overview of the Cloudflare free plan's limitations and their impact on Remediation Component functionality
- - A walkthrough for deploying the Remediation Component within these constraints successfully
-
-### Understanding Cloudflare Free Plan Limitations
-Our Remediation Component integrates with Cloudflare Workers and Workers KeyValue storage, subject to specific thresholds under the free plan.
-For the complete detailed information, refer to Cloudflare's official documentation:
- - [KeyValue storage limits by plan](https://developers.cloudflare.com/kv/platform/limits/)
- - [Workers limits by plan](https://developers.cloudflare.com/workers/platform/limits/)
-
-Key limitations to note for Remediation Component operation on the free plan include:
- - KV write: Up to 1K per day
- - Worker Requests:Up to 100k per day or 1K per minute
-
-*KV write limit of 1K per day*:
-It's the primary limiting factor as the full decisions list (blocklist) passed on to the Remediation Component often exceeds tens of thousands of IPs.
- - It implies that the initial population of decisions in the Worker's KV will be truncated to 1K
- - However, it will still periodically receive new decisions and remove expired ones.
- - So, eventually you'll have many more than 1K IPs in the KV but it still diminishes the immediate effectiveness of the Remediation Component
-
-The *request quota*:
-While the request quota might seem ample, it's essential to remember that both legitimate traffic and potential attack patterns contribute to this total.
- - ⚠️ It's primordial to [properly setup the failmode](#setting-up-the-worker-route-fail-mode)
- - With the failmode set to passthrough, when you reach the Worker request limits your service will stay available, but the Remediation Component won't apply remediation anymore
-
-### Quick Guide : Configuring the Remediation Component on Cloudflare's Free Plan
-To adapt to the free plan's constraints, we can prioritize local decision-making and manual intervention over broader community-driven blocklists.
-Here's how to set it up:
-
-1. *Configuration Setup*: Begin by [auto-generating the Remediation Component configuration](#auto-config-generator).
-2. *Limiting Decision Sources*: Modify the configuration to prioritize decisions generated by your Security Engine and added manually.
- - Using the config parameter [crowdsec.only_include_decisions_from](#only_include_decisions_from)
- ```yaml
-crowdsec_config:
- [...]
- only_include_decisions_from: ["cscli", "crowdsec"]
- ```
-3. *Failmode Configuration*: It's crucial to [configure the failmode properly](#setting-up-the-worker-route-fail-mode) to ensure your service remains operational even when request quotas are reached.
-4. *Testing with manual decisions*: Verify the functionality by adding decisions manually, which should be promptly reflected in KV storage and enforced by the Remediation Component.
- ```bash
- sudo cscli decisions add --ip 192.168.1.1 --type captcha
- ```
- check your decisions has been added using:
-```bash
- sudo cscli decisions list --origin cscli
-```
- And within a few seconds it will be sent to the KV and the proper remediation should be applied to the requests from the specified IP
-
-## Configuration Reference
-
-### `crowdsec_config`
-
-Used to nest the configuration related to crowdsec.
-
-#### `lapi_url`
-> string
-
-The URL of CrowdSec LAPI. It should be accessible from the Remediation Component.
-
-#### `lapi_key`
-> string
-
-It can be obtained by running the following on the machine CrowdSec LAPI is deployed on.
-
-```bash
-sudo cscli -oraw bouncers add cloudflarebouncer # -oraw flag can discarded for human friendly output.
-```
-
-#### `update_frequency`
-> string (That is parseable by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration))
-
-The Remediation Component will poll the CrowdSec every `update_frequency` interval. (default: 10s)
-
-#### `include_scenarios_containing`
-> [ ]string
-
-Ignore IPs banned for triggering scenarios not containing either of provided word.
-
-```yaml title="Example"
-include_scenarios_containing: ["ssh", "http"]
-```
-
-#### `exclude_scenarios_containing`
-> [ ]string
-
-Ignore IPs banned for triggering scenarios containing either of provided word.
-
-```yaml title="Example"
-exclude_scenarios_containing: ["ssh", "http"]
-```
-
-#### `only_include_decisions_from`
-> [ ]string
-
-Only include IPs banned due to decisions orginating from provided sources.
-
-```yaml title="Example"
-only_include_decisions_from: ["cscli", "crowdsec"]
-```
-
-#### `insecure_skip_verify`
-> boolean
-
-Skip verification of the LAPI certificate, usually used for self-signed certificates
-
-#### `key_path`
-> string
-
-Path to the private key file to use for TLS authentication with CrowdSec LAPI.
-
-#### `cert_path`
-> string
-
-Path to the certificate file to use for TLS authentication with CrowdSec LAPI.
-
-#### `ca_cert_path`
-> string
-
-Path to the CA certificate file to use for TLS authentication with CrowdSec LAPI.
-
-### `cloudflare_config`
-
-Cloudflare configuration.
-
-#### `accounts[].id`
-> string
-
-The ID of the Cloudflare account.
-
-#### `accounts[].zones[].zone_id`
-> string
-
-The ID of the Cloudflare zone.
-
-#### `accounts[].zones[].actions`
-> `captcha` | `ban`
-
-Supported actions for the zone.
-
-#### `accounts[].zones[].default_action`
-> `captcha` | `ban` | `none`
-
-Default action to take for the zone.
-
-#### `accounts[].zones[].routes_to_protect`
-> [ ]string
-
-List of routes to protect within the zone. Example value ["*example.com/*"]
-
-#### `accounts[].zones[].turnstile.enabled`
-> boolean
-
-Enable or disable turnstile for the zone.
-
-#### `accounts[].zones[].turnstile.rotate_secret_key`
-> boolean
-
-Rotate the secret key for turnstile.
-
-#### `accounts[].zones[].turnstile.rotate_secret_key_every`
-> string (That is parseable by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration))
-
-Duration for rotating the secret key. Values should be in the format of `1h2m3s`. Example value `168h0m0s` for 7 days.
-
-#### `accounts[].zones[].turnstile.mode`
-
-Mode for turnstile. Supported values ["managed", "invisible", "non-interactive"]. See [cloudflare-docs](https://developers.cloudflare.com/turnstile/reference/widget-types/) for more details.
-
-#### `accounts[].zones[].token`
-> string
-
-Cloudflare account token.
-
-#### `accounts[].zones[].account_name`
-> string
-
-Account name.
-
-#### `worker.log_only`
-> bool
-
-If true, allow all requests but keep track of what would have been blocked in the metrics.
-
-Defaults to `false`
-
-#### `worker.script_name`
-> string
-
-Name to use for the worker script.
-
-Default to `crowdsec-cloudflare-worker-bouncer`.
-
-#### `worker.kv_namespace_name`
-> string
-
-Title of the Worker KV namespace used to create/locate the decision store.
-
-Change this when running multiple bouncer instances on the same Cloudflare account so each instance uses its own KV namespace.
-
-Default to `CROWDSECCFBOUNCERNS`.
-
-#### `worker.decisions_sync_script_name`
-> string
-
-Name to use for the autonomous decisions sync worker script.
-
-Change this when running multiple bouncer instances on the same Cloudflare account so each instance uses its own sync worker.
-
-Default to `crowdsec-decisions-sync-worker`.
-
-#### `worker.analytics_dataset`
-> string
-
-Name of the [Workers Analytics Engine](https://developers.cloudflare.com/analytics/analytics-engine/) dataset the worker writes metric data points to. See the [Metrics](#metrics) section.
-
-Must match `^[a-zA-Z_][a-zA-Z0-9_]{0,63}$`.
-
-Default to `crowdsec_cloudflare_bouncer`.
-
-#### `worker.logpush`
-> bool
-
-Enable logpush for the worker.
-
-Default to no value.
-
-#### `worker.compatibility_date`
-> string
-
-See https://developers.cloudflare.com/workers/configuration/compatibility-dates/.
-
-Default to no value
-
-#### `worker.compatibility_flags`
-> list of strings
-
-See https://developers.cloudflare.com/workers/configuration/compatibility-flags/.
-
-Default to no value.
-
-#### `worker.observability`
-> object
-
-Optional [Workers Observability](https://developers.cloudflare.com/workers/observability/logs/workers-logs/) (logs and traces) configuration. Omit the block to leave the Cloudflare defaults in place.
-
-If a deployed worker cannot have observability applied, a warning is logged but the deploy is not aborted.
-
-```yaml title="Example"
-observability:
- enabled: true
- head_sampling_rate: 1.0 # Sampling rate for head-based observability (0.0 to 1.0)
- traces:
- enabled: true
- head_sampling_rate: 1.0
-```
-
-#### `decisions_sync_worker.cron`
-> string
-
-Cron expression that defines how often the decisions sync worker runs in autonomous mode. This controls the frequency at which the worker fetches the latest decisions from CrowdSec LAPI and updates the KV store.
-
-The cron expression follows standard cron syntax with 5 fields: `minute hour day month day_of_week`
-
-Example values:
-- `*/1 * * * *` - Every minute (default)
-- `*/5 * * * *` - Every 5 minutes
-- `*/10 * * * *` - Every 10 minutes
-
-:::note
-This setting only applies to autonomous mode (when using the `-S` flag). In daemon mode, the `update_frequency` setting is used instead.
-:::
-
-Default to `*/5 * * * *` (every 5 minutes).
-
-
-
-### `prometheus`
-
-Prometheus configuration.
-
-#### `enabled`
-> boolean
-
-Enable or disable Prometheus metrics.
-
-#### `listen_addr`
-> string
-
-Address to listen for Prometheus metrics. Example value `127.0.0.1`
-
-#### `listen_port`
-> string
-
-Port to listen for Prometheus metrics. Example value `2112`
-
-### Others
-
-#### `ban_template_path`
-> string
-
-Path to the ban template file. If not provided, the default template is used. This HTML would be rendered to requests when they are banned.
-
-#### `log_level`
-> `info` | `debug` | `error` | `warning` | `trace`
-
-Log level of the Remediation Component.
-
-#### `log_mode`
-> `stdout` | `file`
-
-Where the log contents are written (With `file` it will be written to `log_dir` with the name `crowdsec-cloudflare-worker-bouncer.log`)
-
-#### `log_dir`
-
-Relevant if `log_mode` is `file`. This determines where to create log file.
-
-
-## Troubleshooting
- - Metrics can be seen at http://localhost:2112/metrics
diff --git a/crowdsec-docs/unversioned/bouncers/cloudflare.mdx b/crowdsec-docs/unversioned/bouncers/cloudflare.mdx
index 1da5d061f..f07a1e0db 100644
--- a/crowdsec-docs/unversioned/bouncers/cloudflare.mdx
+++ b/crowdsec-docs/unversioned/bouncers/cloudflare.mdx
@@ -1,6 +1,7 @@
---
id: cloudflare
-title: Cloudflare
+title: CrowdSec Cloudflare Worker Bouncer
+toc_max_heading_level: 3
---
import Tabs from '@theme/Tabs';
@@ -8,7 +9,6 @@ import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';
import RemediationSupportBadges from '@site/src/components/remediation-support-badge';
-
@@ -19,505 +19,780 @@ import RemediationSupportBadges from '@site/src/components/remediation-support-b
-📚 Documentation
+📚 Documentation
💠 Hub
-💬 Discourse
+💬 Discourse
-:::danger
+
+
+## Overview
+
+This **Remediation Component** uses **Cloudflare Workers** to block or challenge incoming traffic on your Cloudflare zones.
+
+**How it works:**
+- One or more **Cloudflare zones** are bound to a **Remediation Worker** that filters every incoming request.
+- The Remediation Worker looks up the requesting IP in a **Cloudflare KV store** and applies the associated remediation: **ban** (hard block with a custom HTML page) or **Turnstile** (Cloudflare CAPTCHA challenge).
+- A **Sync Worker** periodically fetches the list of IPs to block and keeps the KV store up to date.
+
+**Where do the IPs come from?**
+
+The Sync Worker can pull decisions from two sources — choose the one that fits your setup:
+
+- A **[Blocklist Integration Endpoint](/u/integrations/remediationcomponent)**: a URL served by CrowdSec, no infrastructure required on your side. This is the simplest option.
+- A **[Security Engine](/u/bouncers/intro)** you run in your own infrastructure: the Sync Worker connects to your LAPI to pull decisions directly.
+
+:::warning
+This Remediation Component relies on Cloudflare Workers and the KV store. It works best on a paid Workers subscription. See [Appendix: Cloudflare Free Plan](#appendix-test-with-cloudflare-free-plan) for details.
+:::
+
+## Setup Methods
+
+There are two ways to deploy this bouncer. Both end up with the same Remediation Worker and Sync Worker running inside your Cloudflare account — they differ only in how you install and manage them.
+
+### Self-Hosted GUI (Recommended) {#self-hosted}
+
+A third **Installer Worker** is deployed alongside the other two, hosting a browser-based GUI. You use this GUI for all setup and configuration actions: deploying the workers, binding zones, configuring Turnstile, and updating your CrowdSec endpoint.
+
+**Best for:** Blocklist Integration setups, minimal footprint, no server to maintain.
+
+→ [Jump to Self-Hosted Setup](#self-hosted-setup)
+
+### CLI Bouncer {#cli-bouncer}
+
+A Go binary (`crowdsec-cloudflare-worker-bouncer`) handles deployment and configuration via YAML and command-line flags. It can run in two modes:
+
+- **Daemon Mode**: the Go process runs continuously alongside your Security Engine, syncing decisions directly.
+- **Autonomous Mode**: the Go process deploys the workers once and exits; the Sync Worker then runs on its own inside Cloudflare.
+
+**Best for:** Security Engine setups, Prometheus metrics, advanced configuration (multiple instances, custom resource names).
+
+→ [Jump to CLI Bouncer Setup](#cli-bouncer-setup)
+
+## Cloudflare API Token
+
+Both setup methods require a Cloudflare **user** API token with the following permissions.
+
+:::warning
+Generate a **user** API token via **My Profile → API Tokens** — not the Account API Tokens section, which has different rate limits and will cause issues.
+:::
+
+Click [**this link**](https://dash.cloudflare.com/profile/api-tokens?permissionGroupKeys=%5B%7B%22key%22%3A%22account_settings%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22challenge_widgets%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22user_details%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22workers_kv_storage%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22workers_routes%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22workers_scripts%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22zone%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22dns%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22account_analytics%22%2C%22type%22%3A%22read%22%7D%5D&name=) to open the Cloudflare token creation page with the required permissions pre-selected, or create one manually at [Tokens](https://dash.cloudflare.com/profile/api-tokens) with:
+
+| Permission Group | Item | Permission |
+| ---------------- | ------------------ | ---------- |
+| Account | Turnstile | Edit |
+| Account | Workers KV Storage | Edit |
+| Account | Workers Scripts | Edit |
+| Account | Account Settings | Read |
+| Account | Account Analytics | Read |
+| User | User Details | Read |
+| Zone | DNS | Read |
+| Zone | Workers Routes | Edit |
+| Zone | Zone | Read |
+
+:::info
+By default the token is scoped to all accounts and zones you have access to. We recommend scoping it only to the accounts and zones you intend to protect.
+:::
+
+:::warning
+Each configured zone must have at least one `A` or `AAAA` DNS record. Zones with only `CNAME` records should be excluded from the scope — the bouncer tries to ignore them automatically, but failure to do so may result in higher KV storage charges.
+:::
+
+---
+
+## Self-Hosted Setup
+
+The self-hosted setup deploys three Cloudflare Workers to your account:
-This bouncer isn't actively supported anymore, due to changes to Cloudflare's API rate limitations.
+- **Installer Worker** — hosts the configuration GUI in your browser.
+- **Sync Worker** — runs on a cron schedule, fetching decisions and updating the KV store.
+- **Remediation Worker** — bound to your zones, filters every incoming request.
-You should instead look at the [Cloudflare Workers Bouncer](/u/bouncers/cloudflare-workers).
+Everything runs inside Cloudflare. No server, no daemon, no YAML files.
+### Deploy the Installer
+
+Click the button below to clone the installer into your GitHub or GitLab account and deploy it as a Cloudflare Worker:
+
+[](https://deploy.workers.cloudflare.com/?url=https://github.com/crowdsecurity/cs-cloudflare-worker-bouncer-install)
+
+:::note
+The Cloudflare deploy flow forks the repository into your GitHub or GitLab account before deploying — you need an account on one of these platforms.
+:::
+
+Once deployed, Cloudflare gives you the URL of your Installer Worker. Open it in your browser.
+
+:::info
+The Installer Worker never stores your API token or credentials. They are held only in your browser session.
:::
-A Remediation Component that syncs the decisions made by CrowdSec with CloudFlare's firewall. Manages multi user, multi account, multi zone setup. Supports IP, Country and AS scoped decisions.
+### Configure via the Installer UI
+
+The UI is a three-step wizard. Work through each section top to bottom.
+{/* ADD SCREENSHOT: Full installer UI with all three sections visible, showing step numbers 1–3 */}
+
+---
+#### Step 1 — Cloudflare API Token
-## Installation
+Paste your Cloudflare API token. The installer validates it immediately and confirms all required permissions are present.
-### Repository
+If you don't have a token yet, click **Create token ↗** in the UI — it opens the Cloudflare dashboard with the exact permission set pre-selected (see [Cloudflare API Token](#cloudflare-api-token) above for the full list).
+
+{/* ADD SCREENSHOT: Step 1 expanded with a valid token — green checkmark, "Token valid — permissions confirmed" */}
+
+---
-Packages for crowdsec-cloudflare-bouncer [are available on our repositories](/u/getting_started/installation/linux#repository-installation). You need to pick the package accord to your firewall system :
+#### Step 2 — CrowdSec Integration Endpoint
+
+Paste the URL of your CrowdSec decision source:
+
+- **Blocklist Integration Endpoint** — from the [CrowdSec Console](https://app.crowdsec.net) under your Remediation Component integration. See [Blocklist as a Service](/u/integrations/remediationcomponent).
+- **Security Engine LAPI URL** — the URL of your self-hosted LAPI, reachable from Cloudflare's network.
+
+The installer displays the currently saved endpoint so you can confirm it is correct before proceeding.
+
+{/* ADD SCREENSHOT: Step 2 expanded, endpoint URL field filled in, "Current endpoint used for protection" label visible below */}
+
+To update the endpoint later without reinstalling, paste the new URL and click **Edit** — the Sync Worker's configuration is updated in the KV store immediately.
+
+---
+
+#### Step 3 — Zone Protection
+
+This section lists every zone in your Cloudflare account with its current status.
+
+{/* ADD SCREENSHOT: Step 3 zone list showing one zone "PROTECTED" and one "UNPROTECTED" with Install / Remove buttons */}
+
+**Installing a zone:**
+
+Select one or more unprotected zones and click **Install**. The installer creates the KV namespace (if absent), uploads the Remediation and Sync Workers, sets up the cron trigger, creates the Worker Route for the zone, and optionally creates a Turnstile widget. You can watch each step complete in real time.
+
+{/* ADD SCREENSHOT: Installation in progress — real-time step log with green checkmarks appearing one by one */}
+
+**Turnstile (CAPTCHA):**
+
+Before installing, toggle **Supports CAPTCHA** on a zone to enable Turnstile for it. Protected zones with Turnstile show a **SUPPORTS CAPTCHA** badge. You can toggle it on or off after installation as well.
+
+{/* ADD SCREENSHOT: Protected zone row showing both "PROTECTED" and "SUPPORTS CAPTCHA" badges */}
+
+**Removing a zone:**
+
+Click **Remove** next to a protected zone to unbind the Remediation Worker from that zone and delete its Turnstile widget. The shared KV namespace and worker scripts remain in place for other protected zones.
+
+Click **Uninstall all** to remove all bouncer infrastructure at once (workers, KV, all routes, all Turnstile widgets).
+
+---
+
+## CLI Bouncer Setup
+
+The CLI bouncer is a Go binary (`crowdsec-cloudflare-worker-bouncer`) that manages Cloudflare infrastructure from the command line using a YAML configuration file.
+
+It supports two modes:
+
+- **Daemon Mode** — the process runs continuously, syncing decisions from your Security Engine to the KV store on a regular interval. Workers and KV are cleaned up automatically when the daemon stops.
+- **Autonomous Mode** — the process deploys the Sync and Remediation Workers once and exits. All subsequent decision syncing is handled by the Sync Worker inside Cloudflare on a cron schedule.
+
+:::warning
+After installation, configure the [Worker Route Fail Mode](#setting-up-the-worker-route-fail-mode) to avoid service outages if the worker encounters errors.
+:::
+
+### Installing the CLI Bouncer
+
+#### From CrowdSec Repositories
+
+Packages for `crowdsec-cloudflare-worker-bouncer` are [available on our repositories](/u/getting_started/installation/linux#repository-installation):
+ { label: 'Debian/Ubuntu', value: 'debian' },
+ { label: 'RHEL/Centos/Fedora', value: 'rhel' },
+ ]}
+>
```bash
-sudo apt install crowdsec-cloudflare-bouncer
+sudo apt install crowdsec-cloudflare-worker-bouncer
```
```bash
-sudo yum install crowdsec-cloudflare-bouncer
+sudo yum install crowdsec-cloudflare-worker-bouncer
```
+Then set up the bouncer:
-Then run the following commands to setup your bouncer:
-
+
+
```bash
-sudo crowdsec-cloudflare-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml # auto-generate cloudflare config for provided space separated tokens
-sudo crowdsec-cloudflare-bouncer -s # this sets up IP lists and firewall rules at cloudflare for the provided config.
-sudo systemctl start crowdsec-cloudflare-bouncer # the bouncer now syncs the crowdsec decisions with cloudflare components.
+# Auto-generate config from your Cloudflare tokens
+sudo crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+# Review config and set crowdsec_config.lapi_key
+sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+# Start the daemon
+sudo systemctl start crowdsec-cloudflare-worker-bouncer
```
-:::warning
-
-Please configure your server to emit real IPs rather than cloudflare IPs in logs, so crowdsec can function properly. See how to [here](https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs)
+
+
-:::
+```bash
+# Auto-generate config from your Cloudflare tokens
+sudo crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+# Review config and set crowdsec_config.lapi_key
+sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+# Deploy workers to Cloudflare and exit — no daemon needed
+sudo crowdsec-cloudflare-worker-bouncer -S -c /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+```
-:::info
+In autonomous mode the Go process deploys the configuration to Cloudflare and exits. All decision synchronization is handled by Cloudflare scheduled workers.
-If your component is not installed on the same machine than LAPI, don't forget to set the `crowdsec_lapi_url` and `crowdsec_lapi_key` in the configuration file `/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml`
+
+
+:::warning
+Configure your origin server to emit real visitor IPs rather than Cloudflare IPs in logs, so CrowdSec can function properly. See [Restoring original visitor IPs](https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs).
:::
-:::note
-
-You need to run `sudo crowdsec-cloudflare-bouncer -d` to cleanup exisiting cloudflare components created by component before editing the config files.
-
+:::info
+If the bouncer is not installed on the same machine as LAPI, set `crowdsec_config.lapi_url` and `crowdsec_config.lapi_key` in the configuration file.
:::
:::note
-
-You can run `sudo crowdsec-cloudflare-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml` to generate the configuration by discovering all the accounts and the zones associated with the provided tokens.
-
+Run `sudo crowdsec-cloudflare-worker-bouncer -d` to clean up existing Cloudflare infrastructure before editing the config file.
:::
+#### Manual Installation
-### Manual
-
-#### Assisted
+Download the [latest release](https://github.com/crowdsecurity/cs-cloudflare-worker-bouncer/releases).
-Download the [latest release](https://github.com/crowdsecurity/cs-cloudflare-bouncer/releases).
+
+
```bash
-tar xzvf crowdsec-cloudflare-bouncer.tgz
-cd crowdsec-cloudflare-bouncer/
+tar xzvf crowdsec-cloudflare-worker-bouncer.tgz
+cd crowdsec-cloudflare-worker-bouncer/
sudo ./install.sh
-sudo crowdsec-cloudflare-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml # auto-generate cloudflare config for provided tokens
-sudo crowdsec-cloudflare-bouncer -s # this sets up IP lists and firewall rules at cloudflare for the provided config.
-sudo systemctl start crowdsec-cloudflare-bouncer # the bouncer now syncs the crowdsec decisions with cloudflare components.
+sudo crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+sudo systemctl start crowdsec-cloudflare-worker-bouncer
```
-#### From source
+
+
```bash
-make release
-cd crowdsec-cloudflare-bouncer-vX.X.X
+tar xzvf crowdsec-cloudflare-worker-bouncer.tgz
+cd crowdsec-cloudflare-worker-bouncer/
sudo ./install.sh
+sudo crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+sudo crowdsec-cloudflare-worker-bouncer -S -c /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
```
-Rest of the steps are same as of the above method.
+
+
-## Container
+#### From Source
-Make sure you have docker or podman installed. In this guide we will use docker, but podman would work as a drop in replacement too.
+:::note
+Requires Go >= 1.23
+:::
-### Setup
+
+
```bash
-docker run crowdsecurity/cloudflare-bouncer \
- -g , > cfg.yaml # auto-generate cloudflare config for provided space separated tokens
-```
-
-You can then review the contents of the file `cfg.yaml` and make any necessary changes.
-
-```
-vim cfg.yaml # review config and set `crowdsec_lapi_key`
+git clone https://github.com/crowdsecurity/cs-cloudflare-worker-bouncer
+cd cs-cloudflare-worker-bouncer
+make release
+cd crowdsec-cloudflare-worker-bouncer-*
+./crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+sudo systemctl start crowdsec-cloudflare-worker-bouncer
```
-The `crowdsec_lapi_key` can be obtained by running the following:
+
+
```bash
-sudo cscli -oraw bouncers add cloudflarebouncer # -oraw flag can discarded for human friendly output.
+git clone https://github.com/crowdsecurity/cs-cloudflare-worker-bouncer
+cd cs-cloudflare-worker-bouncer
+make release
+cd crowdsec-cloudflare-worker-bouncer-*
+./crowdsec-cloudflare-worker-bouncer -g , -o /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+sudo vi /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
+./crowdsec-cloudflare-worker-bouncer -S -c /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
```
-The `crowdsec_lapi_url` must be accessible from the container.
+
+
-### Runtime
+### Daemon Mode
-```bash
- docker run \
- -v $PWD/cfg.yaml:/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml \
- -p 2112:2112 \
- crowdsecurity/cloudflare-bouncer
-```
+The Go process runs continuously and:
+1. Creates a Cloudflare Worker and a KV namespace per configured account.
+2. Creates Worker Routes per configured zone — all matching requests are handled by the worker.
+3. Periodically polls your Security Engine or Blocklist Integration and updates the KV store with the latest decisions.
+4. For every incoming request, the Remediation Worker checks the IP, country, and AS against the KV store and applies the matching remediation.
-## Configuration
+*A Blocklist Integration endpoint can be substituted for the Security Engine in the diagram below.*
-Configuration file can be found at `/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml`
+
-```yaml
-# CrowdSec Config
-crowdsec_lapi_url: http://localhost:8080/
-crowdsec_lapi_key: ${API_KEY}
-crowdsec_update_frequency: 10s
-include_scenarios_containing: [] # ignore IPs banned for triggering scenarios not containing either of provided word, eg ["ssh", "http"]
-exclude_scenarios_containing: [] # ignore IPs banned for triggering scenarios containing either of provided word
-only_include_decisions_from: [] # only include IPs banned due to decisions orginating from provided sources. eg value ["cscli", "crowdsec"]
-
-#Cloudflare Config.
-cloudflare_config:
- accounts:
- - id:
- token:
- ip_list_prefix: crowdsec
- default_action: managed_challenge
- total_ip_list_capacity: # only this many latest ip scoped decisions would be kept
-
- zones:
- - actions:
- - managed_challenge # valid choices are either of managed_challenge, js_challenge, block
- zone_id:
-
- update_frequency: 30s # the frequency to update the cloudflare IP list
-
-# Component Config
-daemon: true
-log_mode: file
-log_dir: /var/log/
-log_level: info # valid choices are either debug, info, error
-log_max_size: 40
-log_max_age: 30
-log_max_backups: 3
-compress_logs: true
+:::info
+The Workers and KV created by the bouncer are cleaned up from Cloudflare automatically on daemon stop or launch error.
+:::
-prometheus:
- enabled: true
- listen_addr: 127.0.0.1
- listen_port: 2112
-```
+### Autonomous Mode
-## Making changes to configuration
+The Go process deploys the infrastructure once and exits. No persistent daemon is needed.
-The component creates Cloudflare infra (IP lists, rules etc) according to your config file.
+Two Cloudflare Workers are deployed:
-Before changing the config, always run the following command to clear old infra:
+- `crowdsec-cloudflare-worker-bouncer` (Remediation Worker) — applies cached decisions to incoming requests.
+- `decisions-sync-worker` (Sync Worker) — periodically fetches decisions from CrowdSec using Cloudflare scheduled tasks.
-```
-sudo crowdsec-cloudflare-bouncer -d
-```
+
-### Upgrading from v0.0.X to v0.1.Y
+Once deployed, the Sync Worker runs on the configured cron schedule and keeps the KV store current.
-During v0.0.X there was no `managed_challenge` action, instead `challenge` action was used by bouncer. This is deprecated since v0.1.0 .
+
-This section assumes you used the default config (generated via `crowdsec-cloudflare-bouncer -g ,` )
+
-After upgrading the component from v0.0.X to v0.1.Y , run the following commands to migrate to `managed_challenge`.
+#### Resetting Decisions
-```bash
-sudo crowdsec-cloudflare-bouncer -d
-sudo crowdsec-cloudflare-bouncer -g , -o
-sudo systemctl restart crowdsec-cloudflare-bouncer
-```
-
-
-## Cloudflare Configuration
-
-**Background:** In Cloudflare, each user can have access to multiple accounts. Each account can own/access multiple zones. In this context a zone can be considered as a domain. Each domain registered with cloudflare gets a distinct `zone_id`.
+To reset all decisions in the KV store without redeploying the infrastructure, add a `RESET` key with value `true` to the KV namespace via the Cloudflare dashboard. On the next sync cycle the worker will clear all existing decisions and repopulate from CrowdSec.
+### Metrics
-For obtaining the `token`:
-1. Sign in as a user who has access to the desired account.
-2. Go to [Tokens](https://dash.cloudflare.com/profile/api-tokens) and create the token. The component requires the following permissions to function.
-
+The Remediation Worker writes metric data points to a [Workers Analytics Engine](https://developers.cloudflare.com/analytics/analytics-engine/) dataset, tracking:
-To automatically generate config for cloudflare check the helper section below.
+- Number of requests processed
+- Number of requests blocked
+- Number of requests that threw an exception
+- Average request processing latency
+:::info
+Since `v0.0.18`, metrics are stored in a Workers Analytics Engine dataset instead of a D1 database. Make sure your Cloudflare token has `Account Analytics: Read` permission (see the [permissions table](#cloudflare-api-token)). Without it the metric poll returns a `403` and metrics are disabled, but remediation enforcement is unaffected.
-:::note
-If the zone is subscribed to a paid Cloudflare plan then it can be configured to support multiple types of actions. For free plan zones only one action is supported. The first action is applied as default action.
+If upgrading from an older version, the legacy `CROWDSECCFBOUNCERDB` D1 database is no longer used and can be deleted manually from the Cloudflare dashboard.
:::
-## Helpers
+The dataset name defaults to `crowdsec_cloudflare_bouncer` and can be customized with [`worker.analytics_dataset`](#workeranalytics_dataset).
+
+**In Daemon Mode**, the running process polls the Analytics Engine SQL API, exposes metrics via Prometheus, and pushes them to CrowdSec for `cscli` visualization.
-The component binary has built in helper scripts to do various operations.
+**In Autonomous Mode**, metrics are collected in the Analytics Engine dataset but are **not pushed to CrowdSec**.
-### Auto config generator
+### CLI Helpers
-Generates component config by discovering all the accounts and the zones associated with provided list of tokens.
+#### Auto Config Generator
-Example Usage:
+Generates a configuration file by discovering all accounts and zones associated with your tokens:
```bash
-sudo crowdsec-cloudflare-bouncer -g ,... -o /etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml
+sudo crowdsec-cloudflare-worker-bouncer -g , -o cfg.yaml
+cat cfg.yaml > /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
```
:::note
-This script only generates cloudflare related config. By default it refers to the config at `/etc/crowdsec/bouncers/crowdsec-cloudflare-bouncer.yaml` for crowdsec configuration.
+This only generates Cloudflare-related config. CrowdSec LAPI config must be set manually in the output file.
:::
-Using custom config:
+Using a custom config path:
```bash
-sudo crowdsec-cloudflare-bouncer -c /path/to/config/file -g ,...
+sudo crowdsec-cloudflare-worker-bouncer -c ./cfg.yaml -g ,
```
-### Cloudflare Setup
+#### Cloudflare Cleanup
-This only creates the required IP lists and firewall rules at cloudflare and exits.
+Deletes all Cloudflare infrastructure created by the bouncer:
-Example Usage:
```bash
-sudo crowdsec-cloudflare-bouncer -s
+sudo crowdsec-cloudflare-worker-bouncer -d
```
-### Cloudflare Cleanup
+---
+
+## Setting Up the Worker Route Fail Mode
+
+Worker routes are created with **Fail Closed** mode by default. In this mode, any worker error results in a Cloudflare 1027 error page shown to visitors — including errors caused by plan quota overruns.
+
+There is no public Cloudflare API to change this. **We recommend manually switching all bouncer-created routes to Fail Open**, so that if the worker encounters an error, traffic bypasses it and reaches your origin server normally.
+
+1. Log in to the Cloudflare dashboard and select your account.
+2. Open the website's Overview page.
+3. Click **Worker Routes** in the left menu.
+
+
+
+4. Click the route created by the bouncer, then click **Edit**.
+5. Under **Request limit failure mode**, select **Fail open**.
+
+
+
+---
+
+## Appendix: Test with Cloudflare Free Plan
+
+Using the free plan is feasible but requires understanding its constraints.
+
+### Free Plan Limitations
+
+| Limit | Value | Impact |
+|---|---|---|
+| KV writes | 1,000 / day | Initial blocklist population is truncated to 1K IPs |
+| Worker requests | 100K / day or 1K / min | Heavy traffic may exhaust quota |
+
+The KV write limit is the primary constraint — large blocklists (tens of thousands of IPs) will be truncated on first sync. Incremental updates still flow in over time, so coverage grows gradually.
+
+When the request quota is exhausted, the worker stops applying remediation. This makes [setting Fail Open](#setting-up-the-worker-route-fail-mode) essential — without it your site returns error pages to all visitors once the quota is hit.
+
+### Recommended Free Plan Configuration
+
+1. **Auto-generate config**: [use the config generator](#auto-config-generator).
+2. **Limit decision sources** to your local Security Engine only:
+
+```yaml
+crowdsec_config:
+ only_include_decisions_from: ["cscli", "crowdsec"]
+```
-This deletes all IP lists and firewall rules at cloudflare which were created by the component.
+3. **Set Fail Open** on all worker routes: [instructions above](#setting-up-the-worker-route-fail-mode).
+4. **Test with a manual decision**:
-Example Usage:
```bash
-sudo crowdsec-cloudflare-bouncer -d
+sudo cscli decisions add --ip 192.168.1.1 --type captcha
+sudo cscli decisions list --origin cscli
```
-## How it works
+Within a few seconds the decision appears in the KV store and the remediation is enforced.
-The service polls the CrowdSec Local API for new / deleted decisions. It then makes API calls to Cloudflare to update IP lists and firewall rules depending upon the decision.
+---
## Configuration Reference
-### `crowdsec_lapi_url`
-> string
+### `crowdsec_config`
-The URL of CrowdSec LAPI. It should be accessible from the component.
+```yaml
+crowdsec_config:
+ lapi_key: ${API_KEY}
+ lapi_url: ${CROWDSEC_LAPI_URL}
+ update_frequency: 10s
+ include_scenarios_containing: []
+ exclude_scenarios_containing: []
+ only_include_decisions_from: []
+ insecure_skip_verify: false
+ key_path: "" # TLS client key for LAPI authentication
+ cert_path: "" # TLS client cert for LAPI authentication
+ ca_cert_path: "" # CA cert to trust the LAPI certificate
-### `crowdsec_lapi_key`
-> string
+cloudflare_config:
+ accounts:
+ - id:
+ zones:
+ - zone_id:
+ actions:
+ - captcha
+ default_action: captcha # captcha | ban | none
+ routes_to_protect: []
+ turnstile:
+ enabled: true
+ rotate_secret_key: true
+ rotate_secret_key_every: 168h0m0s
+ mode: managed # managed | invisible | non-interactive
+ token:
+ account_name: owner@example.com
+ worker:
+ log_only: false
+ script_name: ""
+ kv_namespace_name: ""
+ decisions_sync_script_name: ""
+ analytics_dataset: ""
+ logpush: null
+ tags: []
+ compatibility_date: ""
+ compatibility_flags: []
+ observability:
+ enabled: true
+ head_sampling_rate: 1.0
+ traces:
+ enabled: true
+ head_sampling_rate: 1.0
+ decisions_sync_worker:
+ cron: '*/5 * * * *'
+
+log_level: info
+log_media: "stdout"
+log_dir: "/var/log/"
+ban_template_path: ""
-API key to authenticate with the LAPI.
+prometheus:
+ enabled: true
+ listen_addr: 127.0.0.1
+ listen_port: "2112"
+```
-### `cert_path`
+#### `crowdsec_config.lapi_url`
> string
-Path to the certificate file used to authenticate with the LAPI.
+URL of CrowdSec LAPI. Must be accessible from the bouncer.
-### `key_path`
+#### `crowdsec_config.lapi_key`
> string
-Path to the key file used to authenticate with the LAPI.
+Bouncer API key. Obtain it by running on the LAPI machine:
-### `ca_path_file`
-> string
-
-Path to the CA file used to trust the LAPI certificate.
+```bash
+sudo cscli -oraw bouncers add cloudflarebouncer
+```
-### `crowdsec_update_frequency`
-> string (That is parseable by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration))
+#### `crowdsec_config.update_frequency`
+> string (parseable by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration))
-The component will poll the CrowdSec every `update_frequency` interval.
+How often the bouncer polls CrowdSec. Default: `10s`.
-### `include_scenarios_containing`
-> [ ]string
+#### `crowdsec_config.include_scenarios_containing`
+> []string
-Ignore IPs banned for triggering scenarios not containing either of provided word.
+Only include decisions for IPs that triggered scenarios containing one of these words.
```yaml title="Example"
include_scenarios_containing: ["ssh", "http"]
```
-### `exclude_scenarios_containing`
-> [ ]string
+#### `crowdsec_config.exclude_scenarios_containing`
+> []string
-Ignore IPs banned for triggering scenarios containing either of provided word.
+Exclude decisions for IPs that triggered scenarios containing one of these words.
```yaml title="Example"
exclude_scenarios_containing: ["ssh", "http"]
```
-### `only_include_decisions_from`
-> [ ]string
+#### `crowdsec_config.only_include_decisions_from`
+> []string
-Only include IPs banned due to decisions orginating from provided sources.
+Only include decisions originating from these sources.
```yaml title="Example"
only_include_decisions_from: ["cscli", "crowdsec"]
```
-### `cloudflare_config`
-> [CloudflareConfig](https://github.com/crowdsecurity/cs-cloudflare-bouncer/blob/20c902ee1e95fe13135dd493d7e96840bafc931b/pkg/cfg/config.go#L34-L37)
+#### `crowdsec_config.insecure_skip_verify`
+> boolean
-This block contains cloudflare specific config.
+Skip TLS certificate verification for LAPI. Use for self-signed certificates.
-#### `update_frequency`
-> string (That is parseable by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration))
+#### `crowdsec_config.key_path` / `cert_path` / `ca_cert_path`
+> string
-The frequency at which to update the cloudflare resources.
+Paths to TLS client key, certificate, and CA certificate for mutual TLS authentication with LAPI.
-```yaml title="Example"
-update_frequency: "10s"
-```
+### `cloudflare_config`
-#### `accounts`
-> [ ][AccountConfig](https://github.com/crowdsecurity/cs-cloudflare-bouncer/blob/20c902ee1e95fe13135dd493d7e96840bafc931b/pkg/cfg/config.go#L26-L33)
+#### `accounts[].id`
+> string
-List of account of configs
+Cloudflare account ID.
-##### `id`
+#### `accounts[].zones[].zone_id`
> string
-id of cloudflare account
+Cloudflare zone ID.
-##### `token`
-> string
+#### `accounts[].zones[].actions`
+> `captcha` | `ban`
-cloudflare token to use to access the account.
+Remediations supported by this zone.
-##### `ip_list_prefix`
-> string
+#### `accounts[].zones[].default_action`
+> `captcha` | `ban` | `none`
-The prefix to use for naming the IP lists created by the bouncer. The name of IP list will be of the form `ip_list_prefix`+`action`.
+Remediation applied when a decision's type is not in `actions`. Set to `none` to ignore such decisions.
-##### `total_ip_list_capacity`
-> int
+#### `accounts[].zones[].routes_to_protect`
+> []string
-Limit the number of items in IP lists. This is required for avoiding limit of 10k items for lists.
+Routes within the zone to protect. Example: `["*example.com/*"]`
-##### `default_action`
-> `managed_challenge` | `block` | `js_challenge` | `challenge` | `none`
+#### `accounts[].zones[].turnstile.enabled`
+> boolean
-The action to be applied for a decision, if the decision's action is not supported by a zone.
+Enable Turnstile (CAPTCHA) for this zone.
-`default_action` must be supported by all zones.
+#### `accounts[].zones[].turnstile.rotate_secret_key`
+> boolean
-**Example:**
+Automatically rotate the Turnstile secret key.
-Consider your zone config supports the actions `managed_challenge` and `js_challenge`. Your `default_action` is `managed_action`. If you create the following decision:
+#### `accounts[].zones[].turnstile.rotate_secret_key_every`
+> string (parseable by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration))
-```
-sudo cscli decisions add --ip 192.168.1.1 --type ban
-```
+Rotation interval. Example: `168h0m0s` (7 days).
-Since the zone doesn't support `ban` decision type, it'll be inserted into the IP list given by `default_action`. In this case it'll be the list for `managed_challenge`.
+#### `accounts[].zones[].turnstile.mode`
+> `managed` | `invisible` | `non-interactive`
-You can completely ignore such decisions by setting `default_action` to `none`. It won't be inserted into any list then.
+Turnstile widget mode. See [Cloudflare docs](https://developers.cloudflare.com/turnstile/reference/widget-types/).
-**Note:**
+#### `accounts[].token`
+> string
-Following table is mapping of decision type to it's destination IP list.
+Cloudflare API token for this account.
-| Decision Type | Default Action |
-| ------------- | ----------------- |
-| captcha | managed_challenge |
-| ban | block |
-| js_challenge | js_challenge |
+#### `accounts[].account_name`
+> string
+Human-readable account name.
-:::warning
-`challenge` action is deprecated in favour of `managed_challenge`.
-:::
+#### `worker.log_only`
+> bool
-#### `zones`
-> [ ][ZoneConfig](https://github.com/crowdsecurity/cs-cloudflare-bouncer/blob/20c902ee1e95fe13135dd493d7e96840bafc931b/pkg/cfg/config.go#L21-L25)
+If `true`, allow all requests but record what would have been blocked in metrics. Default: `false`.
-This block contains config for each zone to be managed by the component. The zone must be accessible from the parent account.
+#### `worker.script_name`
+> string
-##### `zone_id`
+Name of the Remediation Worker script. Default: `crowdsec-cloudflare-worker-bouncer`.
+
+#### `worker.kv_namespace_name`
> string
-The id of the zone.
+Name of the KV namespace. Change this when running multiple bouncer instances on the same account. Default: `CROWDSECCFBOUNCERNS`.
-##### `actions`
-> [ ]string
+#### `worker.decisions_sync_script_name`
+> string
-List of actions to be supported by this zone. If the zone is not subscribed to premium plan, then only a single action can be given.
+Name of the Sync Worker script. Change when running multiple instances. Default: `crowdsec-decisions-sync-worker`.
-The supported action must include the `default_action` of the parent account.
+#### `worker.analytics_dataset`
+> string
-Valid choice includes either of
-- `block`
-- `js_challenge`
-- `challenge`
-- `managed_challenge`.
+Workers Analytics Engine dataset name for metrics. Must match `^[a-zA-Z_][a-zA-Z0-9_]{0,63}$`. Default: `crowdsec_cloudflare_bouncer`.
-The component creates an IP list for each action. IP list is at account level, so multiple zones with same parent account will share lists for particular action.
+#### `worker.logpush`
+> bool
-:::warning
-`challenge` action is deprecated in favour of `managed_challenge`
-:::
+Enable log push for the worker.
-**Note:**
+#### `worker.compatibility_date`
+> string
-Following table is mapping of decision type to it's destination IP list, which are created according to zone actions
+See [Cloudflare compatibility dates](https://developers.cloudflare.com/workers/configuration/compatibility-dates/).
+#### `worker.compatibility_flags`
+> []string
-| Decision Type | Zone Action |
-| ------------- | ----------------- |
-| captcha | managed_challenge |
-| ban | block |
-| js_challenge | js_challenge |
+See [Cloudflare compatibility flags](https://developers.cloudflare.com/workers/configuration/compatibility-flags/).
+#### `worker.observability`
+> object
+Optional [Workers Observability](https://developers.cloudflare.com/workers/observability/logs/workers-logs/) configuration. Omit to leave Cloudflare defaults in place.
-### `daemon`
-> boolean
+```yaml title="Example"
+observability:
+ enabled: true
+ head_sampling_rate: 1.0
+ traces:
+ enabled: true
+ head_sampling_rate: 1.0
+```
-:::warning
-This field has now been deprecated and is ignored within the component
+#### `decisions_sync_worker.cron`
+> string
+
+Cron schedule for the Sync Worker in autonomous mode. Standard 5-field cron syntax.
+
+Examples:
+- `*/1 * * * *` — every minute
+- `*/5 * * * *` — every 5 minutes (default)
+- `*/10 * * * *` — every 10 minutes
+
+:::note
+Applies only to autonomous mode. In daemon mode, `crowdsec_config.update_frequency` controls sync frequency.
:::
-Run the component as a daemon.
+### `prometheus`
-### `log_mode`
-> `stdout` | `file`
+#### `enabled`
+> boolean
-Where the log contents are written (With `file` it will be written to `log_dir` with the name `crowdsec-cloudflare-bouncer.log`)
+Enable Prometheus metrics endpoint.
-### `log_dir`
+#### `listen_addr`
> string
-Relevant if `log_mode` is `file`. This determines where to create log file.
+Address to bind. Example: `127.0.0.1`.
-### `log_level`
-> `trace` | `debug` | `info` | `error`
+#### `listen_port`
+> string
-Log level for the component.
+Port to bind. Example: `2112`.
-### `compress_logs`
-> `true` | `false`
+### Others
-Compress log files on rotation
+#### `ban_template_path`
+> string
-### `log_max_size`
-> int (in MB)
+Path to a custom HTML template rendered for banned requests. Leave empty to use the default.
-Max size of log files before rotation
+#### `log_level`
+> `info` | `debug` | `error` | `warning` | `trace`
-### `log_max_backups`
-> int
+#### `log_media`
+> `stdout` | `file`
-How many backup log files to keep before deletion (can happen before `log_max_age` is reached)
+With `file`, logs are written to `log_dir/crowdsec-cloudflare-worker-bouncer.log`.
-### `log_max_age`
-> int (in days)
+#### `log_dir`
+> string
-Max age of backup files before deletion (can happen before `log_max_backups` is reached)
+Log directory when `log_media` is `file`.
## Troubleshooting
- - Metrics can be seen at http://localhost:2112/metrics
- - Logs are in `/var/log/crowdsec-cloudflare-bouncer.log` (Default unless changed in config)
- - You can view/interact directly in the ban list either with `cscli`
- - Service can be started/stopped with `systemctl start/stop crowdsec-cloudflare-bouncer`
+
+- Prometheus metrics: `http://localhost:2112/metrics`
+
From 97995a1a6cd9dca327b1778cf68237ecf48150dd Mon Sep 17 00:00:00 2001
From: jdv
Date: Wed, 17 Jun 2026 23:46:55 +0200
Subject: [PATCH 3/5] cleaning up phase 01
---
.../unversioned/bouncers/cloudflare.mdx | 25 ++++++++++++++-----
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/crowdsec-docs/unversioned/bouncers/cloudflare.mdx b/crowdsec-docs/unversioned/bouncers/cloudflare.mdx
index f07a1e0db..2407eb97e 100644
--- a/crowdsec-docs/unversioned/bouncers/cloudflare.mdx
+++ b/crowdsec-docs/unversioned/bouncers/cloudflare.mdx
@@ -25,8 +25,6 @@ import RemediationSupportBadges from '@site/src/components/remediation-support-b
@@ -36,18 +34,33 @@ import RemediationSupportBadges from '@site/src/components/remediation-support-b
Metrics
/>
+:::tip
+ We recommend using the **Self-Hosted GUI** setup method for most users. It is simpler to install and maintain, and it supports both Blocklist Integration and Security Engine setups.
+
+ Jump directly to the Setup section for your preferred method:
+ [Self-Hosted GUI](#self-hosted-setup) **_(Recommended)_**
+ [CLI Bouncer Daemon](#cli-bouncer-setup)
+:::
+
## Overview
This **Remediation Component** uses **Cloudflare Workers** to block or challenge incoming traffic on your Cloudflare zones.
**How it works:**
-- One or more **Cloudflare zones** are bound to a **Remediation Worker** that filters every incoming request.
-- The Remediation Worker looks up the requesting IP in a **Cloudflare KV store** and applies the associated remediation: **ban** (hard block with a custom HTML page) or **Turnstile** (Cloudflare CAPTCHA challenge).
-- A **Sync Worker** periodically fetches the list of IPs to block and keeps the KV store up to date.
+- A list of IPs to remediate _(aka decisions)_ is stored in a **Cloudflare KV store**, each IP associated with an action, either:
+ - **Ban** (hard block with a custom HTML page)
+ - **Captcha** (Cloudflare turnstile challenge)
+- This list is kept up to date either by:
+ - A **Sync Worker** that periodically fetches decisions from CrowdSec.
+ - An installed bouncer script that pushes decisions to the KV store via Cloudflare's API.
+
+**There are two ways to deploy this bouncer:**
+- A fully **Self-Hosted** version with a browser-based GUI **_(recommended)_**
+- A daemon **CLI Bouncer** running beside your Security Engine or as a standalone process.
**Where do the IPs come from?**
-The Sync Worker can pull decisions from two sources — choose the one that fits your setup:
+The source of those decisions
- A **[Blocklist Integration Endpoint](/u/integrations/remediationcomponent)**: a URL served by CrowdSec, no infrastructure required on your side. This is the simplest option.
- A **[Security Engine](/u/bouncers/intro)** you run in your own infrastructure: the Sync Worker connects to your LAPI to pull decisions directly.
From 5557df0e83dbb17c6b00856f8bcea2001db27c07 Mon Sep 17 00:00:00 2001
From: jdv
Date: Thu, 18 Jun 2026 10:57:57 +0200
Subject: [PATCH 4/5] adjustements 02
---
.../unversioned/bouncers/cloudflare.mdx | 32 +++++++++++--------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/crowdsec-docs/unversioned/bouncers/cloudflare.mdx b/crowdsec-docs/unversioned/bouncers/cloudflare.mdx
index 2407eb97e..3a665eae8 100644
--- a/crowdsec-docs/unversioned/bouncers/cloudflare.mdx
+++ b/crowdsec-docs/unversioned/bouncers/cloudflare.mdx
@@ -24,23 +24,17 @@ import RemediationSupportBadges from '@site/src/components/remediation-support-b
💬 Discourse
-
-
-
-
:::tip
- We recommend using the **Self-Hosted GUI** setup method for most users. It is simpler to install and maintain, and it supports both Blocklist Integration and Security Engine setups.
+ We recommend using the **Self-Hosted GUI** setup method for most users. It is simpler to install and maintain.
+ For comparative details, see the [Setup Methods](#setup-methods) section below.
Jump directly to the Setup section for your preferred method:
[Self-Hosted GUI](#self-hosted-setup) **_(Recommended)_**
[CLI Bouncer Daemon](#cli-bouncer-setup)
:::
+:::tip
+ Direct link to generate the necessary Cloudflare token needed for this bouncer: [Cloudflare API Token](https://dash.cloudflare.com/profile/api-tokens?permissionGroupKeys=%5B%7B%22key%22%3A%22account_settings%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22challenge_widgets%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22user_details%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22workers_kv_storage%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22workers_routes%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22workers_scripts%22%2C%22type%22%3A%22edit%22%7D%2C%7B%22key%22%3A%22zone%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22dns%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22account_analytics%22%2C%22type%22%3A%22read%22%7D%5D&name=)
+:::
## Overview
@@ -71,9 +65,21 @@ This Remediation Component relies on Cloudflare Workers and the KV store. It wor
## Setup Methods
-There are two ways to deploy this bouncer. Both end up with the same Remediation Worker and Sync Worker running inside your Cloudflare account — they differ only in how you install and manage them.
+There are two ways to deploy this bouncer. Both end up with the **same Remediation Worker** and **Sync Worker(*)** running inside your Cloudflare account.
+Here's a quick comparative overview:
+
+| | Self-Hosted Installer | CLI Bouncer Daemon |
+|---|---|---|
+| **Initial install** | Github Cloudflare integration | Host-based package install |
+| **Setup** | WebUI | config.yaml auto generation and manual edits |
+| **Decision source** | CrowdSec Blocklist Integration or Security Engine | CrowdSec Blocklist Integration or Security Engine |
+| **Decision sync** | Sync Worker | Bouncer daemon or possibility to use Sync Worker |
+| **Metrics** | Cloudflare Analytics Engine Queries | Prometheus metrics & pushed to CrowdSec Console |
+| **Auth to decisions source** | Basic Auth | Basic Auth or MTLS |
+
+
-### Self-Hosted GUI (Recommended) {#self-hosted}
+### Self-Hosted installer (Recommended) {#self-hosted}
A third **Installer Worker** is deployed alongside the other two, hosting a browser-based GUI. You use this GUI for all setup and configuration actions: deploying the workers, binding zones, configuring Turnstile, and updating your CrowdSec endpoint.
From 03e0c8b9a95bf9243d27c52c1521e1157bdbb258 Mon Sep 17 00:00:00 2001
From: jdv
Date: Fri, 19 Jun 2026 11:52:13 +0200
Subject: [PATCH 5/5] finished first version of doc revamp. ready for review
---
crowdsec-docs/sidebarsUnversioned.ts | 2 +-
.../cf-installer-decisions-endpoint-edit.png | Bin 0 -> 78550 bytes
.../cf-installer-decisions-endpoint-info.png | Bin 0 -> 38623 bytes
.../cf-installer-init-valid-token.png | Bin 0 -> 124379 bytes
.../cloudflare-worker/cf-installer-init.png | Bin 0 -> 77527 bytes
.../cf-installer-set-captcha.png | Bin 0 -> 90233 bytes
.../cf-installer-worker-preview.png | Bin 0 -> 40696 bytes
.../cf-installer-zone-bind.png | Bin 0 -> 126671 bytes
.../unversioned/bouncers/cloudflare.mdx | 115 +++++++++---------
crowdsec-docs/unversioned/bouncers/intro.md | 2 +
10 files changed, 62 insertions(+), 57 deletions(-)
create mode 100644 crowdsec-docs/static/img/bouncer/cloudflare-worker/cf-installer-decisions-endpoint-edit.png
create mode 100644 crowdsec-docs/static/img/bouncer/cloudflare-worker/cf-installer-decisions-endpoint-info.png
create mode 100644 crowdsec-docs/static/img/bouncer/cloudflare-worker/cf-installer-init-valid-token.png
create mode 100644 crowdsec-docs/static/img/bouncer/cloudflare-worker/cf-installer-init.png
create mode 100644 crowdsec-docs/static/img/bouncer/cloudflare-worker/cf-installer-set-captcha.png
create mode 100644 crowdsec-docs/static/img/bouncer/cloudflare-worker/cf-installer-worker-preview.png
create mode 100644 crowdsec-docs/static/img/bouncer/cloudflare-worker/cf-installer-zone-bind.png
diff --git a/crowdsec-docs/sidebarsUnversioned.ts b/crowdsec-docs/sidebarsUnversioned.ts
index 5b11faf1e..cd7bd4838 100644
--- a/crowdsec-docs/sidebarsUnversioned.ts
+++ b/crowdsec-docs/sidebarsUnversioned.ts
@@ -652,7 +652,7 @@ const sidebarsUnversionedConfig: SidebarConfig = {
},
{
type: "doc",
- label: "Cloudflare Workers",
+ label: "Cloudflare",
id: "bouncers/cloudflare",
},
{
diff --git a/crowdsec-docs/static/img/bouncer/cloudflare-worker/cf-installer-decisions-endpoint-edit.png b/crowdsec-docs/static/img/bouncer/cloudflare-worker/cf-installer-decisions-endpoint-edit.png
new file mode 100644
index 0000000000000000000000000000000000000000..62f3c508d3eaa5a6d2bf59ffb7497604a6114e35
GIT binary patch
literal 78550
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ð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ⅅ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