Skip to main content

CrowdSec Cloudflare Worker Bouncer

๐Ÿ“š Documentation ๐Ÿ’  Hub ๐Ÿ’ฌ Discourse

tip

We recommend using the Self-Hosted Installer setup method for most users. It is simpler to install and maintain.
For comparative details, see the Setup Methods section below.

Jump directly to the Setup section for your preferred method:
Self-Hosted Installer (Recommended)
CLI Bouncer Daemon

tip

Direct link to generate the necessary Cloudflare token needed for this bouncer: Cloudflare API Token

Overviewโ€‹

This Remediation Component uses Cloudflare Workers to block or challenge incoming traffic on your Cloudflare zones.

How it works:

  • 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 Install&Config GUI (recommended)
  • A daemon CLI Bouncer running beside your Security Engine or as a standalone process.

Where do the IPs come from?

The source of those decisions

  • A Blocklist Integration Endpoint: a URL served by CrowdSec, no infrastructure required on your side. This is the simplest option.
  • A Security Engine 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 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.

  • Self-Hosted Installer (Recommended)
    • Best For: Blocklist Integration setups, minimal footprint, no server to maintain.
    • Setup: WebUI, no YAML files, no daemon.
  • CLI Bouncer Daemon
    • Best For: Security Engine setups, Prometheus metrics, advanced/IAC configuration (multiple instances, custom resource names).
    • Setup: YAML config file, command-line flags, runs as a daemon or one-shot process.

Here's a quick comparative overview:

Self-Hosted InstallerCLI Bouncer Daemon
Initial installGithub Cloudflare integrationHost-based package install
SetupWebUIconfig.yaml auto generation and manual edits
Decision sourceCrowdSec Blocklist Integration or Security EngineCrowdSec Blocklist Integration or Security Engine
Decision syncSync WorkerBouncer daemon or possibility to use Sync Worker
MetricsCloudflare Analytics Engine QueriesPrometheus metrics & pushed to CrowdSec Console
Auth to decisions sourceBasic AuthBasic Auth or MTLS
warning

After installation, configure the Worker Route Fail Mode to FAIL OPEN to avoid service outages if the worker encounters errors or Cloudflare plan quota overruns.

Cloudflare API Tokenโ€‹

Both setup methods require a Cloudflare user API token with the following permissions.

Click ๐Ÿ”‘ this link to open the Cloudflare token creation page with the required permissions pre-selected, or create one manually at Tokens with:

Permission GroupItemPermission
AccountTurnstileEdit
AccountWorkers KV StorageEdit
AccountWorkers ScriptsEdit
AccountAccount SettingsRead
AccountAccount AnalyticsRead
UserUser DetailsRead
ZoneDNSRead
ZoneWorkers RoutesEdit
ZoneZoneRead
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 uses a Cloudflare github integration to deploy a worker acting as the central install and configuration GUI for the Cloudflare bouncer elements.

The steps are:

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:

Deploy to Cloudflare

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 the Installer Worker will appear in the Workers & Pages section of your Cloudflare dashboard.
Cloudflare gives you the URL of your Installer Worker. Open it to reach the installer GUI.

Install Worker

Configure via the Installer UIโ€‹

Click on your Installer Worker URL to open the installer GUI.
The UI is a three-step wizard. Work through each section top to bottom.

Installer UI


Step 1 โ€” Cloudflare API Tokenโ€‹

info

The Installer never stores your API token or credentials. They are held only in your browser session. If you don't have a token yet, click Create token โ†— in the UI. (see Cloudflare API Token above for the permission list).

Paste your Cloudflare API token. The installer validates it immediately and confirms all required permissions are present.
Upon validation, the installer fetches a list of all zones in your account and their current protection status.

In the following step, you can then select which zones to protect.

Valid Token Lists Zones


Step 2 โ€” CrowdSec Integration Endpointโ€‹

Paste the URL of your CrowdSec decision source:

If you had a previous installation, the installer will pre-fill the field with the currently saved endpoint.
You can edit it to change the endpoint without reinstalling by clicking edit and then update now.

Decisions Endpoint Info Decisions Endpoint Edit


Step 3 โ€” Zone Protectionโ€‹

This section lists every zone in your Cloudflare account with its current status.

Installing a zone:

You can install/uninstall individual zones or select multiple zones for batch install/uninstall.

Zone Install

Turnstile (CAPTCHA):

On installed zones you can activate the ability to have CAPTCHA decisions handled.
Toggle Supports CAPTCHA on a zone enable a style of Turnstile for it (managed, non-interactive, invisible). Protected zones with Turnstile show a SUPPORTS CAPTCHA badge. You can toggle it on or off after installation as well.

Captcha settings

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.

Cleaning up all infrastructure:

Click Uninstall all to remove all bouncer infrastructure at once: unbind all zones, delete the shared KV namespace, and remove the worker scripts.
The installer will prompt for confirmation before proceeding.

The uninstall all also removes infrastructure components from older versions of the cloudflare bouncer (D1 database)


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.

Installing the CLI Bouncerโ€‹

From CrowdSec Repositoriesโ€‹

Packages for crowdsec-cloudflare-worker-bouncer are available on our repositories:

SH
sudo apt install crowdsec-cloudflare-worker-bouncer

Then set up the bouncer:

SH
# Auto-generate config from your Cloudflare tokens
sudo crowdsec-cloudflare-worker-bouncer -g <TOKEN1>,<TOKEN2> -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

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.

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

Run sudo crowdsec-cloudflare-worker-bouncer -d to clean up existing Cloudflare infrastructure before editing the config file.

Manual Installationโ€‹

Download the latest release.

SH
tar xzvf crowdsec-cloudflare-worker-bouncer.tgz
cd crowdsec-cloudflare-worker-bouncer/
sudo ./install.sh
sudo crowdsec-cloudflare-worker-bouncer -g <TOKEN1>,<TOKEN2> -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โ€‹

note

Requires Go >= 1.23

SH
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 <TOKEN1>,<TOKEN2> -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

Daemon Modeโ€‹

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.

A Blocklist Integration endpoint can be substituted for the Security Engine in the diagram below.

Daemon Mode Architecture

info

The Workers and KV created by the bouncer are cleaned up from Cloudflare automatically on daemon stop or launch error.

Autonomous Modeโ€‹

The Go process deploys the infrastructure once and exits. No persistent daemon is needed.

Two Cloudflare Workers are deployed:

  • 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.
Autonomous mode init

Once deployed, the Sync Worker runs on the configured cron schedule and keeps the KV store current.

Autonomous mode sync Autonomous mode full architecture

Resetting Decisionsโ€‹

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โ€‹

The Remediation Worker writes metric data points to a Workers Analytics Engine dataset, tracking:

  • 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). Without it the metric poll returns a 403 and metrics are disabled, but remediation enforcement is unaffected.

If 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 worker.analytics_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.

In Autonomous Mode, metrics are collected in the Analytics Engine dataset but are not pushed to CrowdSec.

CLI Helpersโ€‹

Auto Config Generatorโ€‹

Generates a configuration file by discovering all accounts and zones associated with your tokens:

SH
sudo crowdsec-cloudflare-worker-bouncer -g <TOKEN1>,<TOKEN2> -o cfg.yaml
cat cfg.yaml > /etc/crowdsec/bouncers/crowdsec-cloudflare-worker-bouncer.yaml
note

This only generates Cloudflare-related config. CrowdSec LAPI config must be set manually in the output file.

Using a custom config path:

SH
sudo crowdsec-cloudflare-worker-bouncer -c ./cfg.yaml -g <TOKEN1>,<TOKEN2>

Cloudflare Cleanupโ€‹

Deletes all Cloudflare infrastructure created by the bouncer:

SH
sudo crowdsec-cloudflare-worker-bouncer -d

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.

Worker Route tab

  1. Click the route created by the bouncer, then click Edit.
  2. Under Request limit failure mode, select Fail open.

Fail Open setting


Appendix: Test with Cloudflare Free Planโ€‹

Using the free plan is feasible but requires understanding its constraints.

Free Plan Limitationsโ€‹

LimitValueImpact
KV writes1,000 / dayInitial blocklist population is truncated to 1K IPs
Worker requests100K / day or 1K / minHeavy 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 essential โ€” without it your site returns error pages to all visitors once the quota is hit.

  1. Auto-generate config: use the config generator.
  2. Limit decision sources to your local Security Engine only:
YAML
crowdsec_config:
only_include_decisions_from: ["cscli", "crowdsec"]
  1. Set Fail Open on all worker routes: instructions above.
  2. Test with a manual decision:
SH
sudo cscli decisions add --ip 192.168.1.1 --type captcha
sudo cscli decisions list --origin cscli

Within a few seconds the decision appears in the KV store and the remediation is enforced.


Configuration Referenceโ€‹

crowdsec_configโ€‹

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

cloudflare_config:
accounts:
- id: <ACCOUNT_ID>
zones:
- zone_id: <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: <CLOUDFLARE_ACCOUNT_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: ""

prometheus:
enabled: true
listen_addr: 127.0.0.1
listen_port: "2112"

crowdsec_config.lapi_urlโ€‹

string

URL of CrowdSec LAPI. Must be accessible from the bouncer.

crowdsec_config.lapi_keyโ€‹

string

Bouncer API key. Obtain it by running on the LAPI machine:

SH
sudo cscli -oraw bouncers add cloudflarebouncer

crowdsec_config.update_frequencyโ€‹

string (parseable by time.ParseDuration)

How often the bouncer polls CrowdSec. Default: 10s.

crowdsec_config.include_scenarios_containingโ€‹

[]string

Only include decisions for IPs that triggered scenarios containing one of these words.

Example
YAMLExample
include_scenarios_containing: ["ssh", "http"]

crowdsec_config.exclude_scenarios_containingโ€‹

[]string

Exclude decisions for IPs that triggered scenarios containing one of these words.

Example
YAMLExample
exclude_scenarios_containing: ["ssh", "http"]

crowdsec_config.only_include_decisions_fromโ€‹

[]string

Only include decisions originating from these sources.

Example
YAMLExample
only_include_decisions_from: ["cscli", "crowdsec"]

crowdsec_config.insecure_skip_verifyโ€‹

boolean

Skip TLS certificate verification for LAPI. Use for self-signed certificates.

crowdsec_config.key_path / cert_path / ca_cert_pathโ€‹

string

Paths to TLS client key, certificate, and CA certificate for mutual TLS authentication with LAPI.

cloudflare_configโ€‹

accounts[].idโ€‹

string

Cloudflare account ID.

accounts[].zones[].zone_idโ€‹

string

Cloudflare zone ID.

accounts[].zones[].actionsโ€‹

captcha | ban

Remediations supported by this zone.

accounts[].zones[].default_actionโ€‹

captcha | ban | none

Remediation applied when a decision's type is not in actions. Set to none to ignore such decisions.

accounts[].zones[].routes_to_protectโ€‹

[]string

Routes within the zone to protect. Example: ["*example.com/*"]

accounts[].zones[].turnstile.enabledโ€‹

boolean

Enable Turnstile (CAPTCHA) for this zone.

accounts[].zones[].turnstile.rotate_secret_keyโ€‹

boolean

Automatically rotate the Turnstile secret key.

accounts[].zones[].turnstile.rotate_secret_key_everyโ€‹

string (parseable by time.ParseDuration)

Rotation interval. Example: 168h0m0s (7 days).

accounts[].zones[].turnstile.modeโ€‹

managed | invisible | non-interactive

Turnstile widget mode. See Cloudflare docs.

accounts[].tokenโ€‹

string

Cloudflare API token for this account.

accounts[].account_nameโ€‹

string

Human-readable account name.

worker.log_onlyโ€‹

bool

If true, allow all requests but record what would have been blocked in metrics. Default: false.

worker.script_nameโ€‹

string

Name of the Remediation Worker script. Default: crowdsec-cloudflare-worker-bouncer.

worker.kv_namespace_nameโ€‹

string

Name of the KV namespace. Change this when running multiple bouncer instances on the same account. Default: CROWDSECCFBOUNCERNS.

worker.decisions_sync_script_nameโ€‹

string

Name of the Sync Worker script. Change when running multiple instances. Default: crowdsec-decisions-sync-worker.

worker.analytics_datasetโ€‹

string

Workers Analytics Engine dataset name for metrics. Must match ^[a-zA-Z_][a-zA-Z0-9_]{0,63}$. Default: crowdsec_cloudflare_bouncer.

worker.logpushโ€‹

bool

Enable log push for the worker.

worker.compatibility_dateโ€‹

string

See Cloudflare compatibility dates.

worker.compatibility_flagsโ€‹

[]string

See Cloudflare compatibility flags.

worker.observabilityโ€‹

object

Optional Workers Observability configuration. Omit to leave Cloudflare defaults in place.

Example
YAMLExample
observability:
enabled: true
head_sampling_rate: 1.0
traces:
enabled: true
head_sampling_rate: 1.0

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.

prometheusโ€‹

enabledโ€‹

boolean

Enable Prometheus metrics endpoint.

listen_addrโ€‹

string

Address to bind. Example: 127.0.0.1.

listen_portโ€‹

string

Port to bind. Example: 2112.

Othersโ€‹

ban_template_pathโ€‹

string

Path to a custom HTML template rendered for banned requests. Leave empty to use the default.

log_levelโ€‹

info | debug | error | warning | trace

log_mediaโ€‹

stdout | file

With file, logs are written to log_dir/crowdsec-cloudflare-worker-bouncer.log.

log_dirโ€‹

string

Log directory when log_media is file.

Troubleshootingโ€‹

  • Prometheus metrics: http://localhost:2112/metrics
CrowdSec Docs
We use cookies

This site uses cookies to help us improve your experience. You can accept or decline below.