Introduction
This guide provides non-exhaustive recommendations and general best practices to achieve a comprehensive Layer 7 (L7) / Application Security approach with Cloudflare.
This may be relevant for those seeking similar frameworks, such as CIS Benchmarks.
Some features mentioned are available only through advanced Cloudflare bundles, such as WAF Advanced, Advanced Rate Limiting, or Enterprise features like Enterprise Bot Management.
This guide assumes that your domain is already onboarded to Cloudflare as a Zone and configured using Full Setup, meaning Cloudflare is acting as your authoritative DNS provider. Additionally, it’s recommended to have DNSSEC enabled and being familiar with Zone Holds.
You can find all recommendations, security rules and more in the Cloudflare L7 Best Practices Repository (Database) for quick searches.
Prerequisite Knowledge
Before implementing any of the recommendations below, it helps to be familiar with a handful of Cloudflare fundamentals. Most “why did my rule not match?” or “why is this request still reaching my origin?” situations trace back to one of the following concepts.
Order of Execution (Phases)
Cloudflare products do not all run at the same moment. Each product powered by the Ruleset Engine executes within its own phase, and phases run in a fixed order of execution.
Practical implications:
- Anything produced in a later phase cannot be matched in an earlier one. This is exactly why the
CF-Workerheader cannot be used in WAF Custom Rules (see Mitigate Unauthorized Cloudflare Workers), and why headers added by Request Header Transform Rules are invisible to the WAF. - A Skip action only skips the products or phases you explicitly select. It is not a global “allow everything”, and it cannot undo a phase that already executed.
- Within a phase, rules are evaluated top to bottom and the first terminating action wins. Order matters: narrow Skip / Allow rules at the top, broader mitigations below.
- Response phases (Custom Errors, Managed Transforms, Response Header Transform Rules, Compression Rules, and Rate Limiting Rules that use response information) only run after the origin has responded.
Reference: Phases list and WAF phases.
HTTP/S Network Ports
Cloudflare’s proxy only handles a specific set of HTTP/S ports: 80, 8080, 8880, 2052, 2082, 2086, 2095 for HTTP, and 443, 2053, 2083, 2087, 2096, 8443 for HTTPS. Requests to any other port on a proxied hostname are not handled by Cloudflare’s proxy by default. For that you’d require Spectrum.
- Caching is disabled on the alternative ports (
2052,2053,2082,2083,2086,2087,2095,2096,8880,8443), unless an Enterprise cache rule enables it. - Only ports
80and443are compatible with the China Network. - To prevent HTTP/S requests over non-standard ports from ever reaching the origin, enable the Anomaly:Port - Non Standard Port (not 80 or 443) rule described in Stricter Security Requirements with WAF Managed Ruleset.
- For anything that is not HTTP/S (i.e. SSH, RDP, custom TCP/UDP), use Spectrum rather than gray-clouding / DNS-Only the DNS record, which would expose the origin IP. See Non-HTTP/S Use Cases.
- Because of Cloudflare’s anycast network, port scanners will likely report these non-standard ports as open on Cloudflare IPs. That is shared Cloudflare infrastructure serving many customers, not an open port on your origin.
TCP Connections and Connection Limits
When traffic is proxied, there are two independent TCP connections: client → Cloudflare, and Cloudflare → origin. Each has its own timeouts and connection limits.
- Client-side connections have a 400 second idle timeout, after which Cloudflare sends keep-alive probes and eventually severs the connection with a TCP Reset (RST).
- Origin-side timeouts map directly to the Cloudflare error your users would see: 522 (complete TCP connection at 19s, or TCP ACK timeout at 90s), 520 (keep-alive interval at 30s, or proxy idle timeout at 900s), and 524 (proxy read timeout at 125s — configurable for Enterprise zones — or proxy write timeout at 30s).
- Ensure HTTP keep-alives are enabled on your origin. Cloudflare reuses open TCP connections, and origins that close them aggressively cause avoidable connection resets.
- Smart Shield extends this with connection reuse, batching requests from upper-tier data centers over shared connections and reducing origin connections by roughly 30% on average. This lowers the risk of connection exhaustion at the origin under high traffic.
- URLs are limited to 16 KB, and request and response headers to 128 KB in total. Oversized headers (i.e. large JWTs or cookie jars) are rejected before your rules ever evaluate them.
- Applications should handle disconnections gracefully: capacity balancing, data center maintenance, or node restarts can end a connection even with keep-alives in place.
Note: some TCP connection settings can be customized for Enterprise customers — reach out to your account team.
Cloudflare HTTP Headers
Cloudflare adds, modifies, and removes a number of HTTP headers on proxied traffic. Knowing which is which avoids both broken origin logic and false confidence in spoofable values.
CF-Connecting-IP— andTrue-Client-IPon Enterprise — carry the original visitor IP to the origin. Many prefer them overX-Forwarded-For, which may contain a chain of proxy IPs. Remember to restore original visitor IPs at the origin, otherwise every request appears to come from a Cloudflare IP.CF-Rayis sent to the origin and returned to the visitor, and is the single most useful value when correlating logs or contacting support.CF-Workeridentifies the zone originating a Workers subrequest, but is added after rule evaluation — match oncf.worker.upstream_zoneinstead.Cf-Mitigatedsignals to the client that a request was challenged, which is what makes challenge handling viable for API / AJAX / XHR requests.- Cloudflare may strip a few response headers (
Alt-Svc,X-Accel-*) and may drop request headers with names considered invalid, such as those containing a.(dot) character. - If you do not want visitor IPs forwarded at all, enable the Remove visitor IP headers Managed Transform.
_Note: any header a client sends can be spoofed. Headers added by Cloudflare are only trustworthy at the origin if the origin is locked down to accept traffic exclusively from Cloudflare — see Origin Server Protection.
The /cdn-cgi/ Endpoint
Every proxied domain gets a Cloudflare-managed /cdn-cgi/ endpoint, which cannot be modified or customized. Several products depend on it:
/cdn-cgi/trace— identify the Cloudflare data center serving a request./cdn-cgi/challenge-platform/— Challenges, JavaScript Detections (JSD), and Turnstile./cdn-cgi/image/— image transformations./cdn-cgi/l/email-protection— email address obfuscation./cdn-cgi/rum— Web Analytics.
Recommendations:
- Exclude
/cdn-cgi/from your security rules. Blocking or challenging it breaks Challenges, JSD, and Turnstile, and is one of the most common self-inflicted false positives. - Omit it from vulnerability scans, since some of these endpoints intentionally do not carry certain settings.
- Add
Disallow: /cdn-cgi/to yourrobots.txt, preceded byAllow: /cdn-cgi/image/if you serve transformed images.
Reference: Interaction between Cloudflare challenges and Rules features.
Error Responses
When Cloudflare cannot complete a request, it generates its own error response. This covers all 1xxx error codes and Cloudflare-generated 5xx errors (500, 502, 504, 520-526). 5xx errors generated by your origin server are passed through untouched.
The format follows the client’s Accept header: HTML by default, structured JSON for application/json or application/problem+json, and Markdown for text/markdown. Structured error responses are available.
Why this matters for security:
- API clients and agents receive parseable errors instead of an HTML page they cannot interpret — relevant whenever a mitigation lands on a non-browser client.
- Custom Errors take precedence. An uploaded Error Page is served to every client regardless of
Accept, while Custom Error Rules can match on theAcceptheader, letting you serve JSON to APIs, Markdown to agents, and branded HTML to browsers from the same zone. See Branding.
Note: keep error content generic. Verbose error output leaks stack traces, origin hostnames, and framework versions.
Note: if you intend to validate any of the configurations below with a scanner or a penetration test, first review the Scans and Penetration Testing Policy.
Troubleshooting
- Review the Cloudflare Status page.
- Consult the Troubleshooting section.
- Gather necessary information and contact Cloudflare Support.
- Use Trace to understand the impact of your Cloudflare configurations on specific requests.
Recommendations
In general, in most cases you can create rules with the action set to “Log” for testing purposes. This allows you to review what it matches in the Security Events and fine-tune it as needed before applying a more impactful action, such as “Block”, “Managed Challenge”, or even “SKIP”.
For more information, review the older article Protecting OSI layers.
Rollout Approach and Choosing an Action
Whatever the signal, the safest way to introduce it follows the same progression:
Baseline in Security Analytics / Bot Analytics → log-only where supported →
limited challenge or rate-limit → enforce → monitor false positives →
tune thresholds and exceptions
Then pick the action that matches your confidence in the signal and the cost of a false positive:
| Action | Use when | Avoid when |
|---|---|---|
| Log / simulate | New signal, uncertain impact, customer is baselining | A confirmed active attack is harming the origin |
| Skip | Narrow, known-good traffic needs to bypass a specific security control | Broad bypasses for whole ASNs, countries, or generic bot categories |
| Managed Challenge | Browser traffic is suspicious but may be legitimate | API clients, mobile apps, or machine-to-machine flows that cannot solve browser challenges |
| Interactive Challenge | High-risk browser flow where user friction is acceptable | Conversion-critical flows unless scoped narrowly and monitored |
| Rate Limit | Volumetric abuse, credential stuffing, carding, scraping, API abuse | Solely by IP address for mobile/CGNAT-heavy audiences |
| Block | Confirmed malicious fingerprints, impossible flow, known exploit, or repeat abuse | Ambiguous traffic where false positives would be costly |
| Serve cached content | Public cacheable pages during scraping or traffic spikes | Personalized content, checkout, login, account, admin, or sensitive API responses |
Note: Skip bypasses the specific Cloudflare products or phases you select and nothing else.
WAF Managed Rules
Deploy WAF Managed Ruleset
It’s widely recommended to briefly review and then deploy the Managed Ruleset across the entire Zone. Create specific exceptions, if required.

Note: that it is not recommended to have both Account-level WAF Managed Rules, as well as Zone-level WAF Managed Rules deployed and enabled at the same time as this could lead to confusion when reviewing the Security Events. Preferably, standardize at account level with the Account-level WAF Managed Rules; avoid duplicating the same ruleset at the zone level unless you need zone‑specific overrides.
Reference: Cloudflare Managed Ruleset.
Stricter Security Requirements with WAF Managed Ruleset
For additional and stricter security requirements, deploy some of the following rules:
- Anomaly:Header:User-Agent - Empty with Rule ID b57df4f17f7f4ea4b8db33e20a6dbbd3.
- XSS, HTML Injection with Rule ID 882b37d6bd5f4bf2a3cdb374d503ded0.
- Anomaly:URL:Path - Multiple Slashes, Relative Paths, CR, LF or NULL with Rule ID 6e759e70dc814d90a003f10424644cfb.
- Anomaly:Body - Large with Rule ID 7b822fd1f5814e17888ded658480ea8f, in order to mitigate body payloads which are higher than the processing limit.
- It is generally recommended to add WAF exceptions for this, especially for upload endpoints.
- Anomaly:Port - Non Standard Port (not 80 or 443) with Rule ID 8e361ee4328f4a3caf6caf3e664ed6fe.
- Anomaly:Method - Unusual HTTP Method with Rule ID ab53f93c9b03472ab34a5405d9bdc7d5.
- Anomaly:Method - Unknown HTTP Method with Rule ID 6e2240ffcb87477bbd4881b6fd13142f.
- Including all the Vulnerability scanner activity-related Rules.
- Any other relevant Rules you might need.
Log the payload of matched rules, if required, to help diagnosing the behavior of the rules. The encrypted payloads can be found in the Metadata field in Firewall events logs.
Note: it is also generally recommended to disable (globally or selectively) Browser Integrity Check (BIC), especially to prevent potential false positives with APIs / automated traffic and non-browser endpoints. Use modern detections like WAF Managed Rules and Bot Management instead.
Reference: Security Events and Changelog.
Deploy OWASP Core Ruleset
If required, review and then deploy the OWASP Core Ruleset.
Note: Those types of rules are prone for false positives. For customers also using Zaraz, it is recommended to configure an exception for the configured Zaraz endpoint.

Reference: Handle false positives / Troubleshooting.
WAF Custom Rules
Custom rules give you granular control to tailor your security policy to your application’s specific needs. Rules are executed in order / phases, so place your most important rules at the top.
Allow Verified Bots
It’s ordinarily recommended to have as one of the first top Custom Rules a SKIP Custom Rule, allowing Verified Bots, such as i.e. Search Engine Crawler (like GoogleBot). Only skip what you actually intend to bypass (i.e. All remaining custom rules, All rate limiting rules, or All Super Bot Fight Mode rules), as described in Available skip options.

Expression Preview:
(cf.verified_bot_category in {"Search Engine Crawler" "Search Engine Optimization" "Monitoring & Analytics" "Academic Research" "Security" "Accessibility" "Webhooks" "Feed Fetcher" "Archiver"})
Note: every request with a Verified Bot Category is by definition a Verified Bot, so
cf.bot_management.verified_bot or cf.verified_bot_category in {...}is equivalent tocf.bot_management.verified_botalone and skips all Verified Bots, including theAI Crawler,AI Assistant,AI Search, andAggregatorcategories. Prefer explicitly listing the categories you want to allow.cf.verified_bot_categoryandcf.client.botare available on all plans, whereascf.bot_management.verified_botrequires Bot Management.
References: Verified Bots and Allow traffic from verified bots.
Allow APIs
It’s generally recommended to have as one of the first top Custom Rules a SKIP Custom Rule, allowing your and/or partner APIs (i.e. payment callbacks, PreRender IO, etc.), being as specific and using as many fields as possible. The ultimate goal is to pursue a positive security model by implementing API Shield.

Expression Preview:
(http.host eq "api.example.com" and starts_with(http.request.uri.path, "/api/resources") and http.request.method eq "GET" and cf.waf.score gt 70 and cf.bot_management.score lt 10 and any(http.request.headers["x-api-shield"][*] eq "DEMO"))
Note: a static header value is a shared secret that can leak. Where possible, identify partners by their source IPs in a Custom List (
ip.src in $partner_ips) or with mTLS (cf.tls_client_auth.cert_verified) instead of, or in addition to, a header. Keeping a WAF Attack Score condition in a Skip rule ensures that requests which still look malicious are not exempted from the WAF Managed Rules.
Reference: API Shield.
Redirect to Custom HTML
Using a Custom HTML response type, one can create a redirect to another site for specific requests. In this example, any non-verified bot requests coming from the US are redirected.

Expression Preview:
(ip.src.country eq "US" and not cf.bot_management.verified_bot)
With the action Block, the response type Custom HTML, and a response body such as <head><meta http-equiv='refresh' content='0; URL=https://example.com/'></head>.
Note: the
ip.geoip.*fields (as shown in the screenshot) are deprecated in favor of theip.src.*fields. Existing rules keep working, but useip.src.countryfor new rules.
Block Fallthrough API Requests
In order to truly enforce a Positive Security Model, for any fallthrough action of requests not matching any of the API Shield-managed endpoints, create a WAF Custom Rule similar to the one below, preferably with more specific fields to your API.

Expression Preview:
(http.host eq "api.example.com" and cf.api_gateway.fallthrough_detected)
Note:
cf.api_gateway.fallthrough_detectedistruefor requests that do not match any endpoint saved in Endpoint Management. The dashboard also offers this as the Mitigate API requests to unidentified endpoints rule template. Start with the Log action, so that legitimate endpoints which are not yet saved show up in the Security Events and can be added to Endpoint Management before enforcing.
References: Add a fallthrough rule and Schema Validation.
Visibility into Non-expected Request Methods
In some cases, you want to be specific about what type of HTTP Request Methods are allowed on certain endpoints or coming from specific requests, or even just logging relevant methods for visibility.

Expression Preview:
(http.request.method in {"POST" "PURGE" "PUT" "HEAD" "OPTIONS" "DELETE" "PATCH"})
Once you know which methods your application actually needs, invert the logic into a positive security model and block everything else, adjusting the allowed set per hostname or path:
(http.host eq "www.example.com" and not http.request.method in {"GET" "HEAD" "POST" "OPTIONS"})
Reference: HTTP Method Field.
Mitigate likely Malicious Payloads
Every HTTP/S request receives a WAF Attack Score (WAF ML), indicating the likelihood of containing something malicious related to SQLi, XSS, or RCE attacks. This should be used to complement existing security rules and serve as an additional signal to help mitigate potential attacks.

Expression Preview:
(cf.waf.score lt 20)
Cloudflare recommends against blocking solely based on scores below 50: block the Attack range (scores 1–20, as above, or a stricter threshold such as lt 15) and, if desired, apply a Managed Challenge to the Likely attack range (21–50) only in combination with additional conditions, such as a specific URI path or the bot score.
Reference: WAF attack score.
Mitigate known Open Proxies, Anonymizers, VPNs, Malware, and Botnets
By using the Cloudflare-Managed IP Lists, including your own Custom Lists, you can decide what to do with those IP categories. Generally, one wants to block Botnets and Malware.

Expression Preview:
(ip.src in $cf.anonymizer)
A practical baseline is to block the botnet and malware lists, and to log or challenge the anonymizer lists, which also contain legitimate privacy-conscious users:
(ip.src in $cf.botnetcc or ip.src in $cf.malware)
The available Managed IP Lists are $cf.open_proxies, $cf.anonymizer (Open SOCKS proxies, VPNs, and Tor nodes), $cf.vpn, $cf.malware, and $cf.botnetcc.
Reference: Managed IP Lists.
Note: Blocking VPNs by ASN is error-prone: VPN exit nodes mostly live in general-purpose hosting ASNs that also host legitimate services, and residential or mobile ISP ASNs should never end up on such a list. Prefer the
$cf.vpnand$cf.anonymizerManaged IP Lists; if you must block by ASN, curate your own ASN list and review it regularly.
Mitigate Tor Traffic
In case that Tor traffic – an overlay network for enabling anonymous communication – is unwanted, one can simply mitigate it. Make sure to also disable Onion Routing in this case.

Expression Preview:
(ip.src.continent eq "T1")
Cloudflare assigns the pseudo country and continent code T1 to Tor exit nodes, so ip.src.country eq "T1" is equivalent. Tor exit nodes are also part of the $cf.anonymizer Managed IP List. Since Tor is also used legitimately, consider a Managed Challenge rather than a Block unless your risk profile demands it.
Reference: Onion Routing and Tor support.
Mitigate unwanted ASNs
Any unwanted traffic coming from Cloud ASNs (such as AWS, Azure, GCP, etc.) or other ASNs from which you don’t expect traffic, you might want to mitigate. Use Lists with ASNs to easily manage these. One can also opt for dynamic Managed IP Lists.

Expression Preview:
(ip.src.asnum in {396982 8075 16276 14061})
In this example: Google Cloud (396982), Microsoft Azure (8075), OVH (16276), and DigitalOcean (14061). With a List: (ip.src.asnum in $unwanted_asns).
Note: cloud ASNs also host legitimate integrations (webhooks, partner APIs, monitoring, corporate proxies). Prefer a Managed Challenge or Log action, or scope the rule to sensitive paths (login, sign-up, checkout, API) and exclude Verified Bots (
and not cf.bot_management.verified_bot).
Reference: Custom Lists.
Block High Risk Countries
Block high risk countries like the ones that appear in The Office of Foreign Assets Control (OFAC) List.

Expression Preview:
(ip.src.country in {"AF" "BY" "CF" "CG" "CD" "CI" "CU" "ET" "IR" "IQ" "KP" "LR" "ML" "MM" "SO" "SS" "SD" "SY" "VE" "YE" "ZW" "ER"})
Note: this list is illustrative and must be aligned with your legal and compliance team. Only a few jurisdictions are under comprehensive (country-wide) sanctions; most OFAC programs target specific individuals and entities rather than entire countries, and programs change over time. If you do not intend to block all of Ukraine: the Ukraine-related sanctions are region-specific (i.e. Crimea, Donetsk, and Luhansk). Use the region-level field (
ip.src.subdivision_1_iso_code) instead:
(ip.src.country eq "UA" and ip.src.subdivision_1_iso_code in {"UA-43" "UA-40" "UA-14" "UA-09"})
References: Sanctions List Search, OpenSanctions and Block traffic from specific countries.
Block known Bot User-Agents
Block unwanted requests of user-agents known to be used by bots, such as cURL, python-requests, go-http-client, or even empty user-agents.

Expression Preview:
(lower(http.user_agent) contains "python" or lower(http.user_agent) contains "go-http-client" or lower(http.user_agent) contains "scrapy" or lower(http.user_agent) contains "libwww-perl" or lower(http.user_agent) contains "fasthttp" or lower(http.user_agent) contains "undici" or lower(http.user_agent) contains "curl" or lower(http.user_agent) contains "wget" or http.user_agent eq "")
A single case-insensitive regular expression could be easier to maintain (watch out for the matches operator):
(http.user_agent matches r"(?i)(python|go-http-client|scrapy|libwww-perl|fasthttp|undici|curl|wget)" or http.user_agent eq "")
Note: the
containsoperator is case-sensitive, so wrap the field inlower()(otherwisePython-urllibwould slip through). User-Agent strings are trivially spoofed: treat this rule as hygiene against unsophisticated tooling rather than as bot protection (see Bot Management), and scope it to hostnames where no legitimate automation (your own APIs, partner integrations, monitoring) is expected.
References: Challenge bad bots and Operators.
Restrict Access to Admin Areas and Internal Applications
Restrict access to administrative interfaces – such as the WordPress dashboard (/wp-admin, /wp-login.php) or any /admin path – and to internal applications like employee portals or extranets. The preferred option is a Zero Trust approach with Cloudflare Access, which authenticates the user rather than the network. Where that is not possible, restrict access to specific static source IPs of employees or admins (using a Custom List), to the countries in which you have employees located, or to employees with valid mTLS client certificates. Try to be as specific as possible, combining multiple conditions like hostname, HTTP header, ASN, and HTTP method.

Expression Preview (block everything not coming from the allowlist):
((starts_with(http.request.uri.path, "/wp-admin") or http.request.uri.path eq "/wp-login.php") and not http.request.uri.path eq "/wp-admin/admin-ajax.php" and not ip.src in $allowed_ips)
Note: the allowlist condition must be negated (
not ip.src in $allowed_ips) and paired with the Block action.ip.src in $allowed_ips and ...combined with Block would lock out the allowlisted IPs instead./wp-admin/admin-ajax.phpis excluded because WordPress themes and plugins call it from the public frontend. Consider also blocking/xmlrpc.phpunless you rely on it (i.e. Jetpack or the WordPress mobile app).
For internal applications, restrict by country (and by any other condition that identifies your workforce, or in combination with Cloudflare Access):
(http.host eq "portal.example.com" and not ip.src.country in {"DE" "ES" "US"})
References: Require known IP addresses in site admin area, Allow traffic from IP addresses in allowlist only and Allow traffic from specific countries only.
Block Access to Sensitive Files and Paths
Automated scanners constantly probe for configuration files, version control metadata, backups, and debugging endpoints that should never be publicly reachable. Blocking these at the edge is cheap and rarely produces false positives.
Expression Preview:
((http.request.uri.path contains "/.git" or http.request.uri.path contains "/.svn" or http.request.uri.path contains "/.env" or http.request.uri.path contains "/.htaccess" or http.request.uri.path contains "/.htpasswd" or http.request.uri.path contains "/.DS_Store" or ends_with(http.request.uri.path, ".sql") or ends_with(http.request.uri.path, ".bak") or ends_with(http.request.uri.path, ".old") or http.request.uri.path in {"/wp-config.php" "/phpinfo.php"}) and not starts_with(http.request.uri.path, "/.well-known/"))
Note: keep
/.well-known/reachable, as it is used by ACME (HTTP DCV) certificate validation,security.txt, and similar standards. Adjust the list to your stack, and remember that the real fix is to not have these files on the origin server at all.
Reference: Common use cases for custom rules and Functions.
Mutual TLS Authentication
Block all requests that do not have a valid client certificate for Mutual TLS (mTLS) authentication on a specific hostname. In this scenario, mTLS refers to the connection between the client and Cloudflare.

Expression Preview:
(http.host in {"mtls.example.com" "mtls2.example.com"} and not cf.tls_client_auth.cert_verified)
Additionally, another consideration is to also check if the Client Certificates, generated with the default Cloudflare Managed CA, have been revoked and block those. A revoked certificate still counts as verified (cf.tls_client_auth.cert_verified remains true), which is why the revocation check is required.

Expression Preview:
(http.host in {"mtls.example.com" "mtls2.example.com"} and (not cf.tls_client_auth.cert_verified or cf.tls_client_auth.cert_revoked))
References: Cloudflare Public Key Infrastructure (PKI), CFSSL, API Shield mTLS and Workers mTLS. Check out this Learning Path on mTLS at Cloudflare.
Another interesting use case is to associate specific mTLS hostnames with Client Certificate Serial Numbers (cf.tls_client_auth.cert_serial). This allows for more granular control.

Expression Preview:
(http.host in {"mtls.example.com" "mtls2.example.com"} and cf.tls_client_auth.cert_serial ne "<CLIENT_CERT_SERIAL>")
User-Specific JWT Claim Mitigation
To enhance the security of your API endpoints that rely on JSON Web Tokens (JWT), you can create rules targeting specific JWT claims, such as the user claim.
In this example, requests from admin users based on the user claim are subjected to additional scrutiny by challenging requests flagged as potentially malicious based on their WAF Attack Score.

Expression Preview:
(lookup_json_string(http.request.jwt.claims["<TOKEN_CONFIGURATION_ID>"][0], "user") eq "admin" and cf.waf.score lt 40)
Note:
<TOKEN_CONFIGURATION_ID>is the ID of the JWT Validation token configuration in API Shield, which must exist before the claims can be referenced in rules.
Reference: Issue challenge for admin user in JWT claim based on attack score and API Shield.
Visibility into Automated Bot Traffic
In general, one wants to have visibility into automated traffic. This can be commonly achieved with a LOG action, logging anything with a Bot Score “likely automated” based on available detection engines.

Expression Preview:
(cf.bot_management.score lt 30 and not cf.bot_management.verified_bot and not cf.bot_management.static_resource)
Once the traffic is understood, a common baseline is to Block definitely automated traffic (cf.bot_management.score eq 1) and to Managed Challenge likely automated traffic (scores 2–29), always excluding Verified Bots and explicitly exempting your own API and mobile app traffic (i.e. and not starts_with(http.request.uri.path, "/api/")), which cannot solve Challenges. Customers without Enterprise Bot Management should use Super Bot Fight Mode instead.
References: Bot Management variables and Challenge bad bots.
Mitigating Pretend-Browsers with JavaScript Detections
In scenarios where the BotScore alone may not reliably differentiate between likely human and bots, you can enhance detection by optionally enabling JavaScript Detections (JSD).
Note: JSD can only be applied to HTML responses (
Content-Type: text/html) and it cannot be at the root/first HTML request as the JavaScript needs to be injected first.
When enforced via cf.bot_management.js_detection.passed rules and a Managed Challenge, JSD ensures active verification checks.

Expression Preview:
(http.user_agent matches r"^Mozilla/5\.0.+(Chrome|Safari|Firefox)" and http.request.uri.path eq "/login" and http.request.method eq "POST" and not cf.bot_management.js_detection.passed and not cf.bot_management.verified_bot)
Restricting the rule to POST requests ensures it never matches the first HTML request (the login page itself), which is where the JavaScript gets injected. Always use the Managed Challenge action, since legitimate users may not have passed JSD for benign reasons (ad blockers, disabled JavaScript, network issues).
Note: Test with a logging action before enforcing rules to avoid impacting legitimate traffic. Additionally, the Rule should only apply on critical paths and not on initial landing pages, where JS might have not been injected yet. Never apply it to native mobile app or WebSocket endpoints.
Reference: Enforcing execution of JavaScript detections.
Visibility into IPv6 IPs
Most web applications want to be available via IPv6 IP addresses. However, in case that IPv6 is undesired, customers can mitigate IPv6 IPs through the WAF and also disable IPv6 compatibility, if needed.
Note: that this will also block Cloudflare Workers scripts. In order to allow Workers, review the Mitigate Unauthorized Cloudflare Workers section.

Expression Preview:
(ip.src in {::/0})
Reference: IPv6 compatibility.
Account Takeover (ATO) Detections
To detect and mitigate predictable bot behavior, such as login failures, one can use Detection IDs. This is also available for Rate Limiting Rules.

Expression Preview:
(http.host eq "www.example.com" and starts_with(http.request.uri.path, "/login") and http.request.method in {"POST"} and any(cf.bot_management.detection_ids[*] in {201326592}))
Note: Detection ID
201326592flags clients making a suspicious amount of login failures,201326593a suspicious amount of login attempts. Cloudflare detects common login endpoints automatically; label non-traditional ones withcf-log-inusing Endpoint Labels so the detections apply to them.
Reference: Account takeover detections and Turnstile.
Mitigate Disposable Emails on SignUps
To prevent users from signing up with known disposable emails, Cloudflare’s Disposable Email Check can easily check this behavior and the customer can decide what to do with this: block, challenge, log, rate limit, or even add a request header for the origin server.

Expression Preview:
(http.host eq "www.example.com" and http.request.uri.path contains "/api/user/create" and http.request.method eq "POST" and cf.fraud_detection.disposable_email)
Note: label your sign-up endpoint with
cf-sign-upusing Endpoint Labels if it is not detected automatically.
References: Account Abuse Protection and Cloudflare Fraud Detection.
Mitigate Authentication Requests
Prevent or trigger a different behavior when a user tries to log in (authentication event) with leaked credentials, as per Have I been Pwned (HIBP). Or use different related fields.

Expression Preview:
(cf.waf.auth_detected and cf.waf.credential_check.username_and_password_leaked and starts_with(http.request.uri.path, "/login"))
Note:
cf.waf.auth_detectedistruewhenever Cloudflare detected authentication credentials in the request. A simpler variant that works in several cases is(starts_with(http.request.uri.path, "/login") and http.request.method eq "POST" and cf.waf.credential_check.password_leaked). Cloudflare’s own example uses a Managed Challenge for leaked username-password pairs. Alternatively, forward theExposed-Credential-Checkheader to the origin via the Managed Transform and prompt the user to reset their password.
Reference: Leaked credentials detection.
Time-based Rules
Time-based rules can leverage the http.request.timestamp.sec field to apply logic based on specific time periods (useful for maintenance windows or scheduled security posture changes).
For example, you could block all POST or PUT requests to a particular endpoint during a defined time frame.

Expression Preview:
(http.request.timestamp.sec gt 1734998400 and http.request.timestamp.sec lt 1735171200 and http.request.uri.path contains "/santa" and http.request.method in {"POST" "PUT"})
Or simply Log or Skip (allow) specific requests for a specific time.
Note: The timestamp is represented in UNIX time (epoch time) and consists of a 10-digit value. The dashboard converts the human-readable UTC date into the epoch value for you.
Reference: Configure a rule with the Skip action.
Mitigate Unauthorized Cloudflare Workers
The CF-Worker request header identifies the originating host of a subrequest made by a Cloudflare Workers Subrequest, such as when using the Fetch API.
Do not use CF-Worker in WAF Custom Rules, as it is added after rule evaluation. Instead, use cf.worker.upstream_zone, which holds the same value.
Block a specific Worker:
cf.worker.upstream_zone eq "example.com"
Block all Worker subrequests except from your own Worker:
not (cf.worker.upstream_zone in {"" "your-zone.com"})

Reference: CF-Connecting-IP in Worker subrequests.
Validate Rules before Deploying (Dry Run)
The Ruleset Engine can validate a rule change before it is published. The dashboard does this automatically for Custom Rules and Rate Limiting Rules under Security > Security rules. With the Rulesets API, append the dry_run=true query parameter to any write operation (POST, PUT, PATCH, DELETE, at Zone or Account level) to run the same validation and authorization checks as the real request, without creating, updating, deleting, or publishing anything. Validation covers the expression syntax and the availability of fields, functions, and operators, the action and its parameters, phase compatibility, token permissions, plan entitlements, rule quotas, and resources referenced by the rule (for example a Custom List).
The API token needs the same permission as the real change: Zone > WAF > Edit, scoped to the target Zone (or Account > WAF > Edit for account-level rulesets). The following example validates the intended WAF Custom Rules against the http_request_firewall_custom phase entry point of a Zone:
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/phases/http_request_firewall_custom/entrypoint?dry_run=true" \
--request PUT \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"rules": [
{
"description": "Dry-run validation example",
"expression": "(http.request.uri.path ne \"/robots.txt\" and cf.bot_management.verified_bot)",
"action": "log",
"enabled": true
}
]
}'
A valid request returns 200 with "result": null (nothing was saved); operations that normally return 204 still do. An invalid request returns the same status code and error the real write would have produced, for example a 400 with the parsing error in errors, so you can fix the rule before it ever reaches production. dry_run only accepts true or false; any other value returns 400.
To validate a single new rule without touching the rest of the ruleset, first get the entry point ruleset to obtain its ID, then send the rule object to POST /zones/$ZONE_ID/rulesets/$RULESET_ID/rules?dry_run=true.
Note:
PUT .../entrypointreplaces all rules in the phase entry point ruleset. Withdry_run=truethis is harmless, but never drop the parameter from such a request in automation unless replacing the whole ruleset is the intention. If you manage rules with Terraform, keep in mind thatterraform validateandterraform planonly check the configuration against the provider schema; they do not validate the expression against the Ruleset Engine. Add a dry-run call as a CI check (for example on pull requests) beforeterraform apply.
Reference: Validate rule changes before deployment and Rulesets API.
More Common Use Cases for Custom Rules
Review the get started guide and the common use cases for custom rules for more examples. Additionally, for some use cases or if you are managing many Zones, the Account-level WAF can be a good feature to have. When different teams own different sets of rules, or rules are managed via Terraform, group them into custom rulesets (zone level via API on all plans; account level on Enterprise) instead of one long list of custom rules.
Moreover, monitor and replace insecure JS libraries used in your applications.
Review all the fields reference.
Rate Limiting Rules
Rate limiting is essential for protecting your application from brute-force attacks, denial-of-service, and other forms of abuse.
Before choosing thresholds, use the Request rate analysis tab in Security Analytics to see the request rate distribution per IP or JA3/JA4 fingerprint for the traffic you intend to limit – or start with the Log action and review the Security Events. A few things to keep in mind:
- Counters are kept per Cloudflare data center, not globally: the data center ID is a mandatory, hidden characteristic of every rule. A threshold is therefore enforced per data center, which matters most when the IP address is not part of the characteristics.
- A custom counting expression replaces, rather than extends, the rule expression, so repeat the matching conditions in it. Response fields (status code, response headers) are only available in the counting expression, and rules that use them send matching requests to the origin, bypassing the cache.
- Rate limiting rules run after WAF Custom Rules, so a Skip rule that selects All rate limiting rules bypasses them. The Challenge Passage does not apply to rate limiting rules.
- Rate limit by IP alone only where one IP means one client. For CGNAT, mobile, and corporate proxy audiences, combine IP with NAT support with another characteristic, or key on a session identifier (cookie, API key, JWT claim).
Reference: Rate limiting best practices and Rate limiting rule examples.
IP-based Rate Limiting for Logins
To protect login endpoints from multiple login attempts from the same IP address, rate limit based on the required characteristics.

Expression Preview:
(http.host eq "www.example.com" and starts_with(http.request.uri.path, "/login") and http.request.method eq "POST")
With the same characteristics: IP
Limiting the expression to POST requests counts actual login attempts rather than page loads. For audiences behind CGNAT or corporate proxies, where many users share one IP, use IP with NAT support combined with another characteristic such as Path or Header value of, and prefer a Managed Challenge over a Block for the first tier (i.e. more than 5 attempts per minute).
Reference: Rate limiting parameters and IP with NAT support.
Rate Limiting Uploads
To prevent too many uploads / HTTP requests using POST / PUT / PATCH methods.

Expression Preview:
(http.host eq "www.example.com" and starts_with(http.request.uri.path, "/api/upload") and http.request.method in {"POST" "PUT" "PATCH"})
With the same characteristics: IP and JA3 Fingerprint (the JA3/JA4 characteristics require Enterprise Bot Management; otherwise use IP or IP with NAT support alone)
Reference: Fields reference.
Rate Limit Credential Stuffing
To protect against credential stuffing attacks, it’s generally recommended using a layered-security approach. This rate limiting rule is but one example of several approaches.

Expression Preview:
(http.host eq "www.example.com" and starts_with(http.request.uri.path, "/login") and http.request.method eq "POST")
With the same characteristics: IP and JA3 Fingerprint
Custom Counting Expression:
(starts_with(http.request.uri.path, "/login") and http.request.method eq "POST" and http.response.code in {401 403})
Counting only failed logins (401/403 responses from the origin) means legitimate users who log in successfully are not rate limited by this rule. Cloudflare’s reference implementation uses three tiers with increasing penalties: i.e. 4 failures per minute → Managed Challenge, 10 per 10 minutes → Managed Challenge, 20 per hour → Block for a day.
Note: a custom counting expression does not automatically extend the rule expression, which is why the path and method are repeated in it. Without them, any
401/403response on any URL would increment the counter.
Reference: Protecting against credential stuffing and Find an appropriate rate limit.
Rate Limit Suspicious Logins
Implement rate limiting for suspicious login attempts (authentication events) using leaked credentials, specifically leaked passwords, as per Have I been Pwned (HIBP).

Expression Preview:
(http.host eq "www.example.com" and starts_with(http.request.uri.path, "/login") and http.request.method eq "POST" and cf.waf.credential_check.password_leaked)
With the same characteristics: IP
Cloudflare’s own example combines the leaked credentials fields with the ATO detection IDs: (any(cf.bot_management.detection_ids[*] eq 201326593) and cf.waf.credential_check.username_and_password_leaked).
Reference: Leaked credentials detection and Rate limit suspicious logins with leaked credentials.
Rate Limit OTP, Verification and Password Reset Endpoints
One-time password (OTP), e-mail/SMS verification, and password reset endpoints are brute-forced just like logins, but are frequently forgotten. Count only failed attempts so that users submitting a valid code are never affected.
Expression Preview:
(http.host eq "www.example.com" and http.request.uri.path in {"/api/otp/validate" "/account/verify" "/password-reset"} and http.request.method eq "POST")
With the same characteristics: IP
Custom Counting Expression:
(http.request.uri.path in {"/api/otp/validate" "/account/verify" "/password-reset"} and http.request.method eq "POST" and http.response.code in {401 403})
Use a low threshold, for example 5 requests per minute, with the action Block for 10 minutes. If your endpoint returns 200 for both valid and invalid codes (with the result in the response body), drop the response code condition and use request-based counting with a lower threshold instead.
Reference: Protect OTP and verification endpoints.
Geography-based Rate Limiting
If there are markets from which one does not expect a lot of traffic coming from in general, one could rate limit requests coming from those countries based on IPs or other characteristics.

Expression Preview:
(ip.src.country in {"DE"})
With the same characteristics: IP
Note: the
ip.geoip.countryfield shown in the screenshot is deprecated in favor ofip.src.country.
Reference: Enforcing granular access control.
IPv6-based Rate Limiting
To protect against entire IPv6 Prefixes, rate limit with the custom characteristics using the cidr6 function and specifying the prefix length.

Expression Preview:
(http.host eq "www.example.com" and starts_with(http.request.uri.path, "/login") and http.request.method in {"POST"})
With the same characteristics: Custom: cidr6(ip.src, 48)
ISPs typically hand out a /64, /56, or /48 per subscriber, so cidr6(ip.src, 64) is the most granular per-subscriber bucket, while /48 aggregates larger allocations (and therefore more clients, increasing the false-positive risk). IPv4 addresses are passed through unchanged, so the same rule keeps working for IPv4 clients, or use the cidr function.
Reference: Rules language.
Client Certificate-based Rate Limiting
Rate limit based on the same Client Certificate being used multiple times over a specific period of time, in order to prevent abuse of potentially compromised certificates or devices. This is part of mTLS protection.

Expression Preview:
(http.host in {"mtls.example.com" "mtls2.example.com"} and cf.tls_client_auth.cert_verified)
With the same characteristics: Header value of: Cf-Client-Cert-Sha256
Note: the
Cf-Client-Cert-Sha256header is only present once client certificate forwarding has been enabled for the hostname via the API. Alternatively, use the Custom characteristic with thecf.tls_client_auth.cert_fingerprint_sha256field directly.
Reference: SHA-256 fingerprint of the certificate.
JavaScript Detection-based Rate Limiting
Use JavaScript Detections (JSD) to identify human-like clients by solving a challenge in subsequent HTML requests, flagged as cf.bot_management.js_detection.passed. Since JSD cannot run on the first HTML request, track failed JSD attempts on subsequent HTML responses (Content-Type: text/html) and block or challenge clients i.e. after 5 consecutive failures within a 10-minute window.

Expression Preview:
(not cf.bot_management.js_detection.passed and not cf.bot_management.verified_bot and not cf.bot_management.static_resource)
With the same characteristics: IP
Custom Counting Expression:
(not cf.bot_management.js_detection.passed and not cf.bot_management.verified_bot and not cf.bot_management.static_resource and any(http.response.headers["content-type"][*] contains "text/html"))
Note: the counting expression must repeat the JSD condition, otherwise every HTML response from that IP – including those of clients that did pass JSD – would increment the counter. Verified Bots do not execute JavaScript and are excluded, and so are static resources: because the counting expression uses a response header, matching requests are sent to the origin and bypass the cache, so keep the rule expression as narrow as possible (i.e. add a hostname). Use the Managed Challenge action for the same reasons as in Mitigating Pretend-Browsers with JavaScript Detections.
Reference: Do the Challenge actions support content types other than HTML (for example, AJAX or XHR requests)?.
Cookie-based Rate Limiting
In order to limit the amount of times a cookie can be used, one can rate limit by its characteristics. For example, rate limit session cookies or limit the amount of times a single cf_clearance cookie can be used.

Expression Preview:
(starts_with(http.request.uri.path, "/register") and http.request.method in {"POST"})
With the same characteristics: Cookie value of: cf_clearance
Note: the
cf_clearancecookie lifetime is defined by the Challenge Passage (30 minutes by default; Cloudflare recommends between 15 and 45 minutes). A longer passage means fewer repeated challenges for real users, but also a longer window in which a single solved clearance can be replayed by bots, which this rule mitigates. The Challenge Passage does not apply to rate limiting rules. When rate limiting by cookie, also add a custom rule blocking requests that carry more than one value for that cookie, and validate the cookie at the origin.
Reference: Cloudflare Cookies and Limit reuse of a single cf_clearance cookie.
Rate Limit API Clients by Key or Token
For authenticated APIs, rate limit per API key, bearer token, or session rather than per IP, since one key may be used from many IPs (and many keys from one IP). Use API Discovery or the request rate analysis to find a suitable threshold per endpoint.
Expression Preview:
(http.host eq "api.example.com" and starts_with(http.request.uri.path, "/v1/") and len(http.request.headers["x-api-key"]) gt 0)
With the same characteristics: Header value of: x-api-key (or authorization)
Note: header names must be lowercase when used via the API. Requests without the header fall into their own counter, which is why the expression checks for its presence; unauthenticated requests are better handled by a separate rule keyed on IP. The identifier can also be a cookie, a query parameter, a JSON body field, or a JWT claim.
Reference: Protecting REST APIs.
Rate Limit Clients Generating Errors
Clients producing a high volume of 403 or 404 responses are usually scanners, scrapers, or fuzzers enumerating paths. A rule that counts error responses per client catches this behavior regardless of the tool being used.
Expression Preview:
(http.host eq "www.example.com" and not cf.bot_management.verified_bot)
With the same characteristics: IP
Custom Counting Expression:
(http.host eq "www.example.com" and not cf.bot_management.verified_bot and http.response.code in {403 404})
Apply a Managed Challenge once, for example, more than 20 errors are counted within 1 minute. Since the rule expression is broader than the counting expression, all subsequent requests from that client to the hostname are challenged, not only the erroring ones. Tune the threshold to your application: single-page applications and sites with many broken links generate legitimate 404s, and crawlers are excluded via cf.client.bot / cf.bot_management.verified_bot for the same reason.
Note: because the counting expression uses a response field, requests matching the rule expression are sent to the origin and bypass the cache. On static-heavy hostnames, narrow the rule expression (i.e.
and not starts_with(http.request.uri.path, "/assets/"), orand not cf.bot_management.static_resourcewith Bot Management) to limit the cache impact.
Reference: Limit requests from bots.
More Common Use Cases for Rate Limiting Rules
Review the rate limiting best practices and the rate limiting rule examples for more examples, including complexity-based rate limiting for GraphQL and other expensive endpoints (Enterprise with Advanced Rate Limiting).
Review all the fields reference.
Turnstile
Cloudflare’s Turnstile is a privacy-preserving CAPTCHA alternative that allows challenges anywhere on your site. It runs in standard browsers, including mobile – even native mobile apps – when using WebView. Implicit rendering auto-loads on static pages, while explicit rendering offers control over when and where it appears, ideal for dynamic content or Single-Page Applications (SPAs). Learn more about the differences here.
Enterprise customers can also take advantage of Ephemeral IDs, which can help track bots over longer time periods and rate limit based on these IDs instead of IPs or other characteristics.
Note: While Turnstile can be run in invisible mode, it is recommended to use managed mode for login and signup forms to provide users with a clear indication that an action is taking place. Alternatively, another option is to set it up with interaction-only. Additionally, the Turnstile Siteverify API should be triggered when the user clicks the button, initiating the POST request; (or while / before the user is already filling out the form).
When integrating on mobile, address common issues like WebView configuration and JavaScript interface to ensure smooth functionality.
It is also suggested to integrate Turnstile with WAF and Bot Management.
API / AJAX / XHR Requests
For API protection (AJAX/XHR requests), avoid using Challenges directly. APIs cannot complete interactive challenges, and browser CORS (Cross-Origin Resource Sharing) restrictions prevent smooth handling.
Instead, deploy Turnstile on high-risk frontend pages (e.g., login, checkout) to issue a cf_clearance cookie. Once issued, the cookie allows subsequent API requests to pass WAF evaluation without triggering challenges, preserving both security and usability.
For native or non-browser clients, consider detecting Challenge Page responses and handling them at the application layer.
If Managed Challenge wants to be applied directly to API endpoints, configure Transform Rules to expose the mitigation status by adding CORS:
Access-Control-Allow-Origin: *Access-Control-Expose-Headers: Cf-Mitigated
This enables clients to read the Cf-Mitigated header and adjust behavior accordingly.
Client-Side Security (formerly Page Shield)
Monitor your application’s JavaScript dependencies and get notified of any changes with Cloudflare Client-Side Security.
In general, you would want to periodically monitor resources and cookies running on your application. This is relevant for PCI DSS compliance.
Create Policies to enforce a positive security model, allowing only specific resources.
SSL/TLS Certificates
It is typically recommended to use the Advanced Certificate Manager (ACM) for features like delegated DCV, custom hostnames, total TLS, and Automatic SSL/TLS.
For customers with stricter requirements, additionally, disable the Universal SSL certificate.
Those seeking PCI compliance and granular customization over cipher suites should review the developer documentations, as well as the features TLS 1.3, Minimum TLS Version (TLS 1.2 is the recommended minimum, TLS 1.3 preferred), Automatic HTTPS Rewrites, Always Use HTTPS (or preferably disable HTTP plaintext altogether using HSTS).
Note: Review the Post-Quantum Cryptography (PQC) documentation for quantum-resistant algorithms.
Additionally, it is recommended to configure the origin server to match on origin.
Verify that there’s always a valid and active Edge Certificate for your Zones at all times.
Moreover, enabling HTTP/2, HTTP/3 (QUIC), and HTTP/2 to Origin are performance-related features, but also good for improved security.
Branding
In case that branding is important to you and your business, you are able to use and configure Custom Errors.
Additionally, for the Cloudflare WAF, you are able to configure a custom response for blocked requests.
Analytics & Log Management
You can review matched security rules in the Security Events section. A broader overview of all requests and trends can be found in the Security Analytics section.
For an account-level overview, review the Account Analytics.
Note: Cloudflare Dashboard analytics can likely be sampled.
Note: Exclude the
/cdn-cgi/endpoint from your Security Rules, specifically relevant for challenges.
It is strongly recommended to use Logpush, pushing your logs to storage services (such as R2, S3, or others), SIEMs, or log management providers.
It is highly recommended to set up Notifications to keep up to date with everything, subscribing to incident notifications and periodically review the Cloudflare Status page.
Scans and Penetration Testing Policy
Cloudflare customers may conduct scans and penetration tests (with certain restrictions) on application and network-layer aspects of their own assets.
Two results that regularly show up in reports and are expected behavior rather than findings:
- Non-standard ports reported as open on the resolved IPs. Those are shared Cloudflare anycast IPs serving many customers, not open ports on your origin.
- Warnings on
/cdn-cgi/paths, which are managed by Cloudflare and should be omitted from scans.
You can review all details in the developer documentation.
Automation & User Management
Effective automation and user management are critical for maintaining security, operational efficiency, and governance across your Cloudflare infrastructure.

Infrastructure as Code (IaC) Options
Automate deployments, configuration changes, and rollbacks using these tools:
- Cloudflare API
- SDKs
- Terraform
- For external scripts / uncovered resources, use external.
- If you’re planning to change from Dashboard UI to Terraform, use cf-terraforming.
- Validate the intended rules with a Rulesets API dry run in CI before
terraform apply.
- Pulumi
Note the API rate limits.
User Access Management
Implement least-privilege access control following these best practices:
Role-Based Access Control (RBAC)
Cloudflare provides predefined roles with specific permission sets:
- Super Administrator - Full account access (limit to 2-3 users)
- Administrator - Most administrative functions except billing and membership
- Domain-specific roles - Scoped to individual zones:
- DNS Administrator
- Firewall Administrator
- Cache Administrator
- Analytics Administrator
Important: API permissions differ from Dashboard permissions. Review API token permissions separately.
User Groups
User Groups enable scalable permission management:
- Create groups based on teams or functions (e.g., “DevOps Team”, “Security Team”)
- Assign multiple IAM policies to each group
- Add users to groups instead of managing individual permissions
- Automatic permission inheritance for group members
Authentication Security
Multi-Factor Authentication (MFA)
- Enforce MFA for all users
- Support for TOTP apps
- Support for passkeys / security keys
- Backup codes for recovery scenarios
Single Sign-On (SSO)
- Configure SSO with SAML or OIDC providers
- Automatic user (de)provisioning with SCIM
API Token Management
Use Account-Owned Tokens for processes or services:
- Create service accounts for CI/CD pipelines
- Implement token rotation policies (i.e. 90-day maximum)
- Scope tokens to minimum required permissions
- Use separate tokens for different environments
- Store tokens in secure vaults
Monitoring and Compliance
Audit Logging
Monitor Audit Logs for:
- User login events and MFA usage
- Permission changes and role assignments
- API token creation / deletion
- Configuration modifications
- Failed authentication attempts
Export audit logs via:
Access Reviews
Establish regular review cycles:
- Quarterly: Review Super Administrator and Administrator assignments
- Monthly: Audit API tokens and remove unused ones
- Weekly: Check audit logs for anomalous activity
- Automated: Alert on privilege escalations or new user additions
Compliance Considerations
- Document role assignments for SOC 2, ISO 27001 compliance
- Implement separation of duties
- Maintain access control matrices
- Regular attestation of user access rights
Best Practices Summary
- Principle of Least Privilege: Start with minimal permissions and add as needed
- Use Groups: Manage permissions via groups, not individual users
- Automate Everything: Use IaC for reproducible configurations
- Token Hygiene: Rotate API tokens regularly, use scoped permissions
- Monitor Continuously: Set up alerts for suspicious activities
- Document Policies: Maintain runbooks for onboarding/offboarding
- Regular Audits: Schedule periodic access reviews
- Emergency Access: Define break-glass procedures for incidents
For advanced scenarios, consider implementing:
- Build your own custom RBAC using Cloudflare Workers as an authentication and authorization gateway-layer for fine-grained access control
- Integration with identity governance platforms
- Automated compliance reporting using the GraphQL Analytics API
Note: these are non-exhaustive resources and a generalization of proper user and account management practices. Follow industry-standards and implement appropriate procedures within your organization.
Origin Server Protection
Generally, it’s recommended to properly secure and manage your origin servers.
When migrating to Cloudflare, it’s highly recommended to rotate Origin Server IPs and proxy all DNS records.
There’s a variety of different ways and options explained on the Developer Documentation to protect your origin server, as well as prepare for surges or spikes in web traffic for seasonal events, such as during holidays or product launches.
Note: Review the Post-Quantum Cryptography (PQC) documentation.

Additionally, review and enable the available Managed Transforms options to add some bot protection headers, remove “X-Powered-By” headers, or even create your own Content Security Policy (CSP) with the Transform Rules.
Reference: Encryption modes.
HTTP/S Use Cases
Fighting Automated Requests (Bots)
A general goal is protecting against automated requests and bots – though there are different types of bot attacks and often it’s about fraud detection or solving one of these use cases:
- Credential / Credit Card Stuffing
- Content Scraping
- Inventory Hoarding
- Account Takeover (ATO)
- Fake Account Creation
- Content Spam
The best approach to combating bots depends on the Cloudflare features available and configured, as well as the specific types of bot attacks being observed. DDoS attacks are usually also launched by botnets. Every website is unique, and often so are the attack patterns it faces. In general, fighting bots is a cat-and-mouse game, requiring continuous adaptation to evolving threats. Cloudflare continuously enhances its capabilities based on customer feedback and its Threat Intelligence to try to stay ahead.
Wanted vs. Unwanted Automation
The goal is not to “block all bots”. The goal is to allow useful automation, constrain ambiguous automation, and stop abusive automation. Cloudflare’s own framing is that the important distinction is often what the traffic is doing, not simply whether it is a bot or a human: see Moving past bots vs. humans.
Cloudflare Verified Bots are automated services Cloudflare has identified as generally useful or expected, such as search-engine crawlers and monitoring services. Verified does not automatically mean “desired everywhere”. For example, a search crawler may be allowed on public content but should usually not be allowed to crawl login, checkout, account, admin, or API mutation endpoints unless there is a specific business reason.
| Traffic class | Examples | Default posture | Cloudflare controls |
|---|---|---|---|
| Wanted verified bots | Googlebot, Bingbot, social previews, approved monitoring | Skip only where the business wants this traffic | Verified Bots, WAF Skip rules for narrow paths, customer-managed allowlists for owned systems |
| Declared AI/search/content crawlers | Known AI/search crawlers and content consumers | Allow, block, or constrain based on content/business policy | Detection IDs, AI Crawl Control, Bot Preference Sync, BotBase for Operators, robots.txt |
| User-directed agents | Browser or assistant traffic acting on behalf of a real user | Prefer risk-based controls; avoid blanket blocking when behavior is legitimate | Bot Management variables, Turnstile, Clearance / pre-clearance, application-layer step-up |
| Unknown automation | curl, Python requests, commodity headless browsers, suspicious TLS/browser fingerprints | Baseline → challenge/rate-limit → block if abusive | Bot Score, JavaScript Detections, JA3/JA4 fields, WAF Custom Rules, Rate Limiting Rules |
| Malicious fraud bots | Credential stuffing, carding, fake signups, scraper farms, inventory abuse | Block, rate-limit, challenge, or route to origin fraud workflow | Account takeover Detection IDs, Leaked Credentials Detection, Turnstile Ephemeral IDs, API Shield |
Important distinctions:
- Verified bot means Cloudflare recognizes the bot category/operator. It does not mean every request from that bot is business-approved for every endpoint.
- Low Bot Score is a risk signal, not a complete fraud verdict. Stronger actions should combine Bot Score with endpoint sensitivity, method, velocity, Detection IDs, JA3/JA4, leaked credentials, session/user context, and business impact.
- User-Agent is not identity. It is easy to spoof. Prefer Cloudflare-provided bot signals, request behavior, cryptographic or verified signals where available, and application context.
Layered Mitigation Approach
To effectively mitigate bot traffic, consider the following (non-exhaustive) layered-security approach:
- Allow (skip) Verified Bots or Verified Bot Categories.
- Allow (skip) your
sitemap.xml,robots.txtand RSS feed (if applicable) to everyone. - Block or mitigate unwanted bots (i.e. AI bots) by leveraging Bot Management fields in combination with other security controls, solutions (i.e. Snippets) and fields.
- Example honeypot for bots.
- Enable JavaScript Detections (JSD) and enforce them if possible.
- If enforcement isn’t feasible (i.e. for native mobile apps), consider implementing Turnstile (in WebView for mobile) alongside the WAF or Cloudflare’s new mobile SDK (Enterprise feature), which can be combined with API Shield JWT Validation at the edge or programmatically with Snippets.
- Analyze heuristics using Security Analytics and available fields to build WAF Custom Rules based on your needs and signals.
- Deploy the WAF Managed Rules regularly updated security rules.
- For important endpoints, we also recommend WAF Attack Score enforcement.
- Identify ASN patterns and block unwanted traffic from certain networks or cloud providers (i.e. AWS or GCP)l if no legitimate traffic is expected from them.
- Use Managed IP Lists for dynamic mitigations.
- Use (Body) Payload Inspection to perform more detailed mitigations.
- Apply Rate Limiting based on IP and other characteristics to prevent abuse and credential stuffing attacks.
- This is often combined with Leaked Credentials Detection.
- For APIs, implement a positive security model with API Shield, including Schema Validation and Sequence Mitigation.
- Alternatively, one can use Sequence Rules (or also called Cookie-based Sequences) to track and enforce the order of requests a user has made and the time between requests.
- Caching anything possible (non-user-specific content) can also help reduce the load and resources of the origin servers.
- Cloudflare is gradually rolling out Fraud Detection features, such as disposable email checks.
- Additional bot-related configurations can be adjusted by Cloudflare’s Bot Team on a case-by-case basis when talking to the Support Team.
Some additional interesting methods include:
- Delay action
- Send suspect bots to a honeypot
- Turnstile with Workers
- Data loss prevention
- Forward specific HTTP Request Headers with Managed Transforms Rules for the origin to act on.
Non-HTTP/S Use Cases
There are scenarios where users might want to leverage Cloudflare’s global network and robust L3/L4 DDoS protection without handling TLS termination or processing HTTP/S traffic and payloads. For these specialized use cases, Cloudflare offers several options tailored to non-HTTP/S requirements.
Available Options:
-
Grey-Clouded DNS Records
This involves setting a DNS record to “DNS Only” mode. However, this approach is not recommended, as it exposes the origin server’s IP address, compromising security. -
Spectrum
A powerful TCP/UDP proxy solution that supports various non-HTTP/S protocols.- It is typically recommended to enable Proxy Protocol.
- To avoid unintended handling of TLS, ensure the Edge TLS Termination option is disabled.
-
Privacy Gateway
Privacy Gateway uses the Oblivious HTTP (OHTTP) standard to hide client IPs during backend interactions. Acting as a trusted relay, it forwards encrypted messages between clients and servers without accessing their content, ensuring enhanced privacy and anonymity. -
Magic Transit
Operating at the network layer, Magic Transit offers advanced DDoS protection and traffic management without TLS termination.- For a deeper dive, check out the Magic Transit Reference Architecture.
Additional Resources
For more guidance on avoiding Cloudflare TLS termination and decryption, visit the Cloudflare Data Localization FAQ.
Performance Matters Too!
For application performance recommendations, see: General Application Performance Recommendations.
Disclaimer
Educational purposes only.
This blog post is independently created and is not affiliated with, endorsed by, or necessarily representative of the views or opinions of any organizations or services mentioned herein.
The images used in this article primarily consist of screenshots from the Cloudflare Dashboard or other publicly available materials, such as Cloudflare webinar slides.
The guidelines provided in this post are intended for general educational purposes. They should be customized to fit your specific use cases and traffic patterns. You are responsible for configuring settings according to your unique requirements, and it is important to understand their potential impact. Familiarity with Cloudflare concepts such as WAF Phases, Proxy Status, and other relevant features is recommended.
The author of this post is not responsible for any misconfigurations, errors, or unintended consequences that may arise from implementing the guidelines or recommendations discussed herein. You assume full responsibility for any actions taken based on this content and for ensuring that configurations are appropriate for your specific environment.
For additional learning resources, explore the following:
- Learning Paths
- Enterprise Customer Portal (for Enterprise customers)
- Security Center
For tailored support, and in general, if you have any questions or need assistance, please contact Cloudflare support, or (also for non-customers) call the Under Attack (UA) Hotline in emergency situations.