Part 16 built out the Zero Trust access layer: Cloudflare Access policies, identity provider integration, and the mechanics of protecting internal applications without a traditional VPN. If you followed that section closely, you now have a solid picture of who gets in. This part covers what happens to everyone else.
Public infrastructure attracts automated traffic the way a porch light attracts moths. Most of it isn't human. Some of it is benign, crawlers indexing your content or monitoring services checking uptime. A lot of it isn't. Bots probing login forms, scrapers hammering your API, vulnerability scanners looking for anything exposed and unpatched. The question isn't whether your infrastructure is being targeted. It is. The question is whether you've configured anything to do something about it before that traffic reaches your origin server.
That's what this part covers. Firewall rules, the Web Application Firewall, rate limiting, and the basics of bot management. Not as isolated features, but as a layered system where each component handles a different class of threat. By the end, you'll have the mental model and the concrete expressions to start filtering intelligently, without locking out legitimate users in the process.
Why Traffic Filtering Is the Last Line Before Your Origin
The threat landscape for public-facing infrastructure
Anything with a public IP address and an open port is a target. That's not alarmist, it's just arithmetic. Automated scanning tools continuously sweep the internet, logging what's running, what versions are exposed, and what endpoints respond to specific payloads. Your application doesn't need to be famous to attract this traffic. It just needs to exist.
The threat categories break down roughly into five types. Volumetric DDoS floods your connection with raw traffic until legitimate requests can't get through. Credential stuffing replays leaked username and password combinations against login endpoints at scale. Scrapers extract content or pricing data at a rate that degrades performance for real users. Vulnerability scanners probe for known CVEs, misconfigured headers, and exposed admin paths. Injection attacks send crafted payloads in query strings, headers, or request bodies, looking for SQL injection, cross-site scripting, or path traversal opportunities.
No single tool stops all of these. That's the honest starting point.
Where Cloudflare sits in your defense stack
Cloudflare operates at the edge, meaning your traffic passes through Cloudflare's network before it ever touches your origin server. This positioning is the entire value proposition for security. A threat that gets blocked at the edge never consumes your server's CPU, never touches your application code, and never generates a database query.
The security stack inside Cloudflare isn't a single wall. It's a sequence of decision points. DDoS protection absorbs volumetric attacks automatically. Firewall rules (now called custom rules in the updated dashboard) let you write your own traffic logic. The WAF applies signature-based detection against known attack patterns. Rate limiting caps request volume from individual sources. Bot management scores and classifies automated traffic. These layers evaluate in order, and a request that passes one can still be stopped by the next.
Understanding which layer handles which threat class is what separates a thoughtful configuration from a collection of rules that either miss attacks or break your own application. The tradeoffs are real. Aggressive filtering reduces attack surface and increases false positives. Permissive filtering keeps availability high and leaves gaps. Every threshold you set is a judgment call, not a correct answer.
Understanding the Cloudflare WAF: Concepts Before Configuration
What a Web Application Firewall actually does
A Web Application Firewall operates at Layer 7, the application layer. It reads HTTP requests, inspects their contents, and makes decisions based on what's inside: the URI path, query parameters, headers, cookies, and request body. A network firewall works at Layers 3 and 4, caring about IP addresses and ports. The WAF doesn't care which port you're on. It cares what your request is asking the application to do.
The practical distinction matters. A network firewall can block an IP address or a port range. It can't tell the difference between a legitimate search query and a SQL injection attempt embedded in that query. The WAF can. It looks at the payload and compares it against known attack signatures, behavioral patterns, and rule logic to decide whether the request looks like an attack.
Managed rulesets vs. custom rules
Cloudflare's WAF ships with two major categories of rules. Managed rulesets are maintained by Cloudflare and updated automatically as new attack patterns emerge. You don't write these. You enable them and configure their sensitivity. The two primary managed rulesets are the Cloudflare Managed Rules, which cover Cloudflare's own threat intelligence, and the OWASP Core Rule Set, which targets the standard vulnerability classes: injection, cross-site scripting, path traversal, and others documented in the OWASP Top 10.
Custom rules are what you write yourself. They use Cloudflare's expression language to match specific traffic patterns that managed rulesets don't cover, like blocking requests to a path that only your internal tooling should reach, or challenging traffic from a specific country during an active incident.
Free vs. Paid WAF Access
Free plan accounts get access to a limited set of managed rules, primarily DDoS-adjacent protections. The full Cloudflare Managed Rules and the OWASP Core Rule Set require a Pro plan or higher. Custom firewall rules are available on all plans, though the number of rules you can create scales with your subscription tier.
How WAF rules are evaluated in order
Rule evaluation follows a defined pipeline. Cloudflare processes rules in phase order, with custom rules in http_request_firewall_custom evaluated before managed rules in http_request_firewall_managed. Within each phase, rules are evaluated by priority, highest priority number first.
Each rule has an action that fires when the rule matches. The available actions are Block (drop the request, return a 403), Interactive Challenge (present a CAPTCHA), JS Challenge (run a JavaScript browser check), Managed Challenge (Cloudflare decides which challenge type is appropriate), Log (record the match without taking action), and Skip (bypass subsequent rules for this request). The first matching rule with a terminal action wins. A request that matches a Block rule never reaches the managed rulesets below it. This ordering behavior is what makes rule priority a configuration decision you need to think about, not just a default to accept.
Custom Firewall Rules: Writing Your Own Traffic Logic
The Cloudflare Rules language and expression syntax
Cloudflare's expression language looks intentionally familiar if you've spent time with Wireshark filters or Berkeley Packet Filter syntax. It's a structured boolean expression that evaluates fields against values using comparison operators. The logic reads left to right, combines with and, or, and not, and groups with parentheses.
A simple expression looks like this:
(ip.geoip.country eq "CN" and cf.threat_score gt 20)That matches requests originating from China with a threat score above 20. You can extend it:
(ip.geoip.country eq "CN" and cf.threat_score gt 20) or
(http.user_agent contains "sqlmap")Now it matches either condition. The language is expressive enough to handle most traffic filtering scenarios without requiring you to write code or manage server-side configuration files.
Fields you can match on: IP, ASN, country, URI, headers, user-agent
The field library is broad. The ones you'll use most frequently:
ip.src: The source IP address. Accepts individual IPs or CIDR notation. ip.geoip.country: Two-letter country code. Useful for geographic blocking during incidents. ip.geoip.asnum: The Autonomous System Number. Lets you block entire hosting providers or known abuse networks. cf.threat_score: Cloudflare's internal threat score, 0 to 100, based on Project Honey Pot data and internal signals. http.request.uri.path: The path portion of the URL, without query string. http.request.uri.query: The query string. Useful for catching injection payloads in parameters. http.user_agent: The User-Agent header. Scanner tools and bots often send identifiable strings. http.request.headers: Full header access, including custom headers your application might send.
Actions: block, challenge, JS challenge, managed challenge, log, skip
Choosing the right action matters as much as writing the right expression. Block is terminal and immediate. The request gets a 403 and nothing else. Use it when you're certain the traffic is malicious and false positives are acceptable. JS Challenge serves a page that runs JavaScript in the browser. Automated tools without a real browser engine fail it. Legitimate users pass it transparently in most cases. Interactive Challenge presents a visible CAPTCHA. Higher friction, higher confidence. Managed Challenge lets Cloudflare choose between JS and Interactive based on its own risk assessment. It's a reasonable default when you're not sure which challenge type fits.
Log is the action most people skip and shouldn't. It records every match without blocking anything. Deploy a new rule in Log mode first, watch the analytics for 24 to 48 hours, and confirm you're matching what you intended before switching to Block or Challenge.
Free Plan Rule Limits
Free accounts can create up to 5 custom firewall rules. Pro plans allow 20. Business plans allow 100. Enterprise has no practical limit. If you're on a free plan, prioritize rules that cover the highest-impact attack surfaces first: login endpoints, admin paths, and API authentication routes.
Skip bypasses rules below the current one for matching requests. It's how you build allow-first patterns. If you want to challenge all traffic from a country but exempt your own office IP, the Skip rule for your IP goes first with a higher priority, and the country challenge rule sits below it.
Blocking Obvious Bad Traffic: Practical Examples
Blocking by threat score
cf.threat_score is a number between 0 and 100 that Cloudflare assigns to each request based on the source IP's history. The data comes from Project Honey Pot, a network of honeypot servers that log malicious activity, combined with Cloudflare's own internal signals from traffic patterns across its global network. A score of 0 means no known bad history. A score above 50 means the IP has been observed doing things that honeypots record.
The practical threshold most configurations start with is 30. Above that, you're catching IPs with documented histories of scraping, scanning, or sending spam. The rule expression is straightforward:
(cf.threat_score gt 30)Action: Managed Challenge. Not Block. Threat score alone isn't a certainty. An IP that scored 35 two months ago because someone used it to scrape a forum might now belong to a legitimate user on a residential connection that rotated through a bad pool. Managed Challenge gives real users a path through while stopping automated tools that can't solve it.
Blocking known bad ASNs and IP ranges
ASN blocking is blunter than threat score but more reliable for specific scenarios. If you're seeing sustained abuse from traffic originating in a particular datacenter or hosting provider, blocking the ASN stops the entire network range without requiring you to enumerate individual IPs.
"Blocking an ASN is a business decision, not just a security decision. Some legitimate users route through datacenter IPs. Know what you're cutting off before you cut it."
A rule targeting a specific ASN:
(ip.geoip.asnum eq 14061)That expression matches DigitalOcean's ASN. If your application has no reason to receive traffic from cloud hosting ranges and you're seeing abuse from those IPs, it's a defensible block. If your API serves developers who might be testing from cloud instances, it isn't. Context determines whether the rule makes sense.
Filtering by user-agent patterns
Scanner tools announce themselves in their User-Agent strings with surprising consistency. sqlmap, nikto, masscan, nuclei, and zgrab all send identifiable strings by default. A rule targeting them:
1(http.user_agent contains "sqlmap") or
2(http.user_agent contains "nikto") or
3(http.user_agent contains "masscan")Action: Block. These aren't tools that legitimate users run from browsers. A request carrying a sqlmap user-agent string is a vulnerability scan, and there's no ambiguity worth preserving with a challenge. Block it, log the match, and move on. Sophisticated attackers can spoof user-agents, so this rule catches the unsophisticated majority. It's not a complete defense. It's a filter that eliminates noise so your logs surface the more interesting traffic.
Protecting Login Endpoints with Targeted Rules
Why login pages are high-value targets
Login endpoints are the most attacked surface on most web applications. The reason is simple: a successful credential stuffing attack converts directly into account access, and the attacker doesn't need to find a vulnerability in your code. They just need a leaked credential list and a login form that accepts POST requests. Billions of username and password pairs from prior data breaches circulate continuously in automated attack tooling. Your application doesn't need to have been breached for its login page to be targeted with credentials from someone else's breach.
Brute-force attacks try password variations against a single account. Credential stuffing tries known pairs against many accounts. Both generate high POST volumes against a single path. Both are detectable and blockable at the edge before they touch your authentication logic.
Building a login-specific firewall rule
Scoping a rule to a specific path keeps the blast radius small. You're not challenging all POST traffic. You're challenging POST requests to the login path from IPs outside your trusted ranges.
The expression:
(http.request.uri.path eq "/login" and http.request.method eq "POST" and not ip.src in {192.168.1.0/24})Action: JS Challenge. This fires on every POST to /login that doesn't originate from your trusted internal range. Real users in a browser pass the JS Challenge without noticing it. Automated credential stuffing tools that don't run a full browser engine fail it and move on.
For WordPress installations, the path changes but the logic doesn't:
(http.request.uri.path eq "/wp-login.php" and http.request.method eq "POST")The same pattern applies to any sensitive endpoint. /admin, /api/auth, /checkout, /account/password-reset. Any path where a successful automated request has direct business impact deserves its own scoped rule.
Layering challenge and rate limiting on authentication paths
A JS Challenge on the login path stops most automated tools. Rate limiting stops the ones that pass the challenge or that generate enough volume to matter even at low per-request success rates. The two work differently: the challenge filters by bot capability, rate limiting filters by
Bot Traffic Basics: Separating Good Bots from Bad Bots
The word "bot" carries a lot of baggage. Mention bots in a security conversation and people immediately picture credential stuffing attacks or scraping operations draining your bandwidth. But Googlebot is a bot. Bingbot is a bot. Your uptime monitor is a bot. Block the wrong ones and your site disappears from search results or your on-call team stops getting alerts. The goal isn't to stop all bots. It's to stop the ones that aren't supposed to be there.
The Bot Spectrum: Verified Good Bots, Unverified Bots, Malicious Bots
Cloudflare assigns every request a bot score on a scale from 1 to 99. A score near 1 means Cloudflare is highly confident the request comes from a bot. A score near 99 means it almost certainly comes from a human. The middle range is where things get complicated. Unverified bots, scrapers operating with legitimate-looking headers, and misconfigured crawlers all cluster in that gray zone.
The cf.client.bot field cuts through some of that ambiguity. It's a boolean that returns true only when Cloudflare has cryptographically verified the bot's identity against a known list of legitimate services. Googlebot, Bingbot, common uptime monitors, and a handful of other services qualify. If cf.client.bot is true, the request is from a verified good actor. Full stop.
A practical rule that challenges suspicious automated traffic without touching legitimate crawlers looks like this:
(cf.threat_score lt 30 and not cf.client.bot)Set the action to JS Challenge rather than Block. That gives legitimate but unverified bots a path through while adding friction for the junk.
Using cf.client.bot and Bot Score Fields
Always structure your bot rules with the verified-bot exception first. The cf.client.bot check should appear in every bot-related rule as a negative condition. Skipping it is how teams accidentally de-index their own sites.
Watch Your Own Integrations
CI/CD health checks, synthetic monitoring tools, and third-party payment webhooks often look like bots to Cloudflare's scoring engine. Identify those source IPs before deploying any bot score rule and add them to an allowlist skip rule that fires before your bot rules run.
Free vs. Paid Bot Management Capabilities
Free plans get Bot Fight Mode, a binary toggle that blocks requests Cloudflare identifies as definitely-bad bots. It's better than nothing, but it offers no tuning, no score visibility, and no exceptions beyond the verified-bot list.
Pro plans unlock Super Bot Fight Mode, which adds controls for definitely-automated, likely-automated, and verified-bot traffic separately. You can challenge rather than block, which reduces false-positive risk significantly.
Full Bot Management with machine learning scoring is an Enterprise add-on. It includes behavioral analysis, fingerprinting across sessions, and a richer scoring model trained on Cloudflare's global traffic. For most projects, Super Bot Fight Mode on Pro handles the realistic threat surface without the Enterprise price tag.
Enterprise Feature Note
The full Bot Management product, including ML-based scoring and bot analytics dashboards, is not available on Free, Pro, or Business plans. If your threat model requires it, factor that into your infrastructure budget before assuming it's included.
Country, IP, and Geographic Rules: Precision and Pitfalls
Geographic filtering sounds simple. Traffic from a country you don't serve keeps triggering your WAF. Block the country. Problem solved. Except it isn't that simple, and treating it like it is will create problems that are harder to diagnose than the original attack traffic.
When Geo-Blocking Makes Sense (and When It Doesn't)
There are legitimate reasons to apply geographic rules. Compliance requirements sometimes mandate that certain data or services stay within specific regions. A regional service with no international user base has no reason to accept traffic from 140 countries it doesn't operate in. Active attack campaigns originating from a specific country can justify a temporary challenge rule while you investigate.
That's the honest limitation. VPNs, Tor exit nodes, and commercial proxy services all defeat IP-based geolocation. A determined attacker won't be stopped by a country block. What geo-blocking actually does is raise the cost of casual attacks and reduce noise from opportunistic scanning. That has real value. Just don't mistake it for a complete solution.
Overly broad country blocks carry SEO risk. Googlebot operates from US-based infrastructure primarily, but Bingbot and other crawlers sometimes originate from international data centers. A sweeping continental block without a cf.client.bot exception can suppress crawling. Always verify your verified-bot exception is in place before enabling geographic rules.
Building Country-Based Rules in Cloudflare
The ip.geoip.country field accepts two-letter ISO country codes. A rule challenging all traffic from a specific country looks like this:
(ip.geoip.country eq "CN" and not cf.client.bot)The ip.geoip.continent field works similarly for broader regional filtering. Use continent-level rules carefully. Europe contains your GDPR obligations. Blocking an entire continent to suppress attack traffic is a blunt instrument with real collateral damage.
Pair geo-rules with cf.threat_score for more surgical filtering. A country that generates high-threat-score traffic is a better candidate for blocking than one that simply generates volume. The combination of country origin plus elevated threat score gives you a defensible, specific rule rather than a broad geographic sweep.
IP Allowlists and Blocklists at Scale
For trusted IPs, use the Skip action rather than Allow. Skip bypasses all subsequent WAF rules for matching traffic, which is what you want for your own office IPs, monitoring services, and payment processor webhooks.
When your allowlist or blocklist grows beyond a handful of entries, use Cloudflare Lists. The IP Lists feature lets you maintain a named list of addresses and reference it in rules with ip.src in $list_name. Managing 200 trusted IP ranges inside individual rule expressions is a maintenance disaster. Lists solve that.
Avoiding Overblocking: The Biggest Mistake in Firewall Configuration
Every WAF deployment eventually produces a false positive. A legitimate user gets blocked. A monitoring bot gets challenged and fails. A payment processor webhook stops reaching your application. The question isn't whether overblocking will happen. It's how quickly you catch it and how much damage it causes before you do.
Common Overblocking Patterns and Their Business Impact
Threat score thresholds set too low are the most common cause. Threat scores aggregate signals from multiple sources, and scores in the 10 to 25 range can include legitimate users on shared corporate networks, users behind certain ISPs, and people browsing from countries with limited internet infrastructure. Setting your challenge threshold at 10 will catch a lot of real users.
Broad country blocks hit SEO crawlers, business partners, and international customers who were never part of the attack surface you were trying to close. One missed exception can de-index a page or break a B2B integration that someone's quarterly report depends on.
Aggressive rate limits on shared IPs affect everyone behind a NAT gateway. A university network, a corporate proxy, or a mobile carrier NAT can have thousands of users sharing a single IP. A 10-requests-per-minute rate limit on that IP blocks an entire campus.
High-Risk Overblocking Targets
Payment processor webhooks, uptime monitors, search engine crawlers, and CI/CD pipeline health checks are the four categories most commonly broken by aggressive WAF rules. Identify the source IPs for all four before you write a single block rule.
The Observe-Then-Enforce Workflow
Cloudflare's Log action is the most underused tool in the WAF configuration interface. A rule set to Log fires, records the match in Security Events, and then lets the request through. No user impact. Full visibility.
"Deploy in Log mode first. Watch what the rule actually catches. Then decide if blocking is justified."
That workflow takes discipline because the temptation after seeing attack traffic is to block immediately. Resist it. Forty-eight hours of Log data will show you legitimate services matching your rules that you hadn't considered. That data is worth more than the two days of exposure.
Testing Rules Safely Before Going Live
The Security Events dashboard (formerly Firewall Events) shows every rule match with the triggering field values, the matched rule ID, and the action taken. Filter by rule ID to see exactly what a specific rule is catching before you switch it from Log to Block.
The deployment sequence that minimizes risk: write the rule, set action to Log, observe for 48 hours, review Security Events for false positives, add exceptions as needed, then switch to Challenge or Block. That sequence isn't slow. It's how you avoid a 2 AM call about a broken payment flow.
Free vs. Paid Feature Boundaries: What You Actually Get
Cloudflare's free tier is genuinely useful. It's also genuinely limited, and the limitations matter for anyone running a production application with real security requirements. Knowing exactly where the plan boundaries fall prevents the unpleasant surprise of discovering a feature you budgeted for isn't available at your tier.
Free Plan Security Capabilities
The free tier includes 5 custom WAF rules, basic Bot Fight Mode, one rate limiting rule, and always-on unmetered DDoS protection. That last point deserves emphasis. DDoS protection is free regardless of plan. Cloudflare doesn't meter it or gate it behind a paid tier. For volumetric attack defense, the free plan is fully equipped.
Five custom rules is enough to get started. It's not enough to implement the full hardening checklist in this article. You'll need to prioritize, and you'll run out of rules before you run out of things to protect.
Pro and Business Plan Additions
Pro at $20 per month is where the security posture changes substantially. Twenty custom rules, Super Bot Fight Mode, and access to managed rulesets including the OWASP Core Rule Set and Cloudflare's own managed ruleset. Those managed rulesets alone justify the cost. They're maintained by Cloudflare's threat intelligence team and updated continuously. Writing equivalent coverage from scratch in custom rules would take weeks and would still be less current.
Business at $200 per month raises the custom rule limit to 100 and adds custom WAF rule sets and advanced bot analytics. For teams managing multiple applications with distinct security profiles, the rule capacity matters.
Workers as a Free-Tier Extender
Cloudflare Workers can implement logic that would otherwise require paid WAF rules. Rate limiting, header inspection, and basic bot detection are all achievable in a Worker on the free Workers tier. It requires more code than a WAF rule, but it's a real option for teams with budget constraints.
Enterprise-Only Features
Enterprise adds unlimited custom rules, Bot Management with ML scoring, Exposed Credentials Check, Account Takeover Protection, and the ability to build custom managed rulesets. These aren't incremental improvements. They're different capabilities built on different infrastructure.
For most projects, Pro covers the realistic threat surface. Enterprise features solve problems at scale that most applications don't encounter until they're generating significant traffic.
Hardening Checklist: Your Security Rule Deployment Workflow
Part 16 covered the structural foundations of WAF rules and rate limiting logic. This checklist translates everything in this article into a sequenced deployment workflow. Work through it in order. The sequence matters because skip rules must exist before block rules, and log mode must precede enforcement.
Pre-Deployment Checklist
Post-Deployment Monitoring Steps
What's Next: Moving from Rules to Zero Trust Enforcement
This part covered a lot of ground. WAF rule structure, threat scores, rate limiting patterns, bot scoring and the cf.client.bot field, geographic filtering with its real limitations, the observe-then-enforce workflow that prevents overblocking, and the feature boundaries across Cloudflare's plan tiers. That's the full picture of what edge filtering looks like when it's configured deliberately rather than reactively.
Threat score rules catch known bad actors. Bot rules handle automated traffic. Rate limits protect specific endpoints. Geo-rules reduce noise from irrelevant regions. Each layer compensates for the gaps in the others. A WAF that relies on a single rule type will always have exploitable gaps.
Part 18 moves from reactive filtering into proactive access control. Zero Trust Access policies operate at a different layer than WAF rules. Where WAF rules filter anonymous traffic at the edge, Zero Trust policies control access to authenticated surfaces: internal tools, staging environments, admin panels, and APIs that should never be publicly reachable. The two systems work together, and understanding how they complement each other changes how you think about the boundary between "public web" and "protected application."
Start every new rule in Log mode. Maintain your allowlist before you write your first block rule. One missed entry can take down a payment processor or silence your monitoring stack at the worst possible moment.