Cloudflare Application Services - Lab Guide (Legacy)

Unified Lab Guide & Technical Reference. Courseware Version 4.0 (AcmeCorp Edition), Legacy / Cloudflare-Managed Lab. Prepared for AcmeCorp IT Security & Infrastructure. Scenario: Edge Security Modernization.

⚠ This is the Legacy (safety-net) edition

This edition targets the Cloudflare-managed lab (the labs.cloudflare.com "Implement: Application Services" pod). Use it only if you cannot provision the self-hosted lab environment that the primary guide targets. Because the managed lab shares one fixed origin that you do not control, a few chapters are Concept-only (Load Balancing, Advanced Caching, Threat Intel, Spectrum, and the origin-sealing capstone) and a few individual steps are presented as concepts rather than hands-on tasks. Everything else is fully hands-on.

You do not install anything or manage cloud servers. Your Cloudflare account and one pre-onboarded Enterprise zone are provided for you, and you run all terminal commands from the in-browser Ubuntu workstation described below.

Typographical Conventions

This guide uses the following conventions to distinguish between user input, system output, and explanatory notes.

ConventionMeaningExample
BoldNames of selectable items in the web interfaceClick Security to open the Security Rule window.
MonospaceText that you enter and coding examplesEnter the following command: dig example.com
Result textLab step results and explanations, expected system outputHTTP/2 200 OK
ItalicsContextual notes explaining "why" a task is necessaryThis ensures the legacy application is not exposed directly to the internet.
<in angle brackets>A variable parameter. The value is defined in the guide or by your instructor.Type ping <student-domain> and press Enter.

How to Use This Lab Guide & The AcmeCorp Scenario

AcmeCorp is a market leader in mock company frontends, and its infrastructure has outgrown its security. Its origin server sits on a public IP address, serving both the corporate website and a public orders API, with no CDN, no real encryption, and no security layer in front of it. It shows: browsers throw certificate warnings, traffic spikes knock the site offline, attackers openly probe the site, scrape pricing, leak sensitive files, and fuzz the API, and because the origin IP is public, anyone can reach it directly and walk around any protection added later.

You are AcmeCorp's new Lead Application Security Engineer. Your mandate is to modernize the edge with Cloudflare Application Services, changing the origin as little as possible, and take AcmeCorp from exposed to defended. Over the next 18 labs you will prove how exposed the origin is, route and encrypt its traffic, make it fast and resilient, build a layered perimeter (WAF, bot management, rate limiting, API Shield, threat intelligence, and client-side protection), then seal the origin so the perimeter cannot be bypassed, and replay the opening attacks to prove nothing gets through.

Each lab solves one concrete part of that story. Reading the lab titles from top to bottom tells it end to end: exposed, onboarded, encrypted, fast, resilient, defended, sealed, and proven. Every lab opens with a "This chapter" box, highlighted in yellow, that states the part of the story you are about to solve.

In this managed lab, your Cloudflare account already contains one pre-onboarded Enterprise zone assigned to you (its name follows the pattern adjective-noun.sxplab.com, for example happy-balancer.sxplab.com). The zone is onboarded (Enterprise plan, Cloudflare nameservers assigned) but starts with no DNS records: you create them as the labs need them. You create the apex record (<student-domain>) in Lab 2 to publish the AcmeCorp website, then add public-api.<student-domain> (the orders API used in the API Shield labs) and ps.<student-domain> (the Page Shield demo page) in the labs that use them. All of these resolve to the shared origin that Cloudflare operates for the class.

ComponentAddressRole
Ubuntu workstationin-browser (Guacamole)The machine you drive the labs from. Open its Terminal to run curl, dig, and openssl, and to simulate attacks. It is reached through your browser; there is no SSH and nothing to install.
Shared web origin20.88.188.200Serves the AcmeCorp website (apex) and, via a name-based vhost, the public API. This is the raw origin IP you attack directly in Lab 1. It is shared by the class and you cannot reconfigure it.
Orders API origin4.157.169.241The backend for the orders API used in Lab 14. You create an api DNS record pointing here.
One shared origin, not your server. Unlike the self-hosted edition, the origin here is a shared machine operated by Cloudflare for the class. You never log in to it, and you cannot change its configuration or firewall. That is why steps that require origin-side changes (installing an Origin CA certificate, stopping a web server to test failover, sealing the origin behind a firewall) are presented in this edition as Concept walkthroughs rather than hands-on tasks.
Fill in here: your lab value

Your instructor assigns you a student domain (the name of your pre-onboarded zone). Type it below and every matching placeholder across this entire guide, including inside the copy blocks, is replaced with your real value. The value saves to your browser only; nothing is sent anywhere.

Lab 1 - The Exposed Origin

Before you deploy a single Cloudflare feature, you attack AcmeCorp the way the open internet already can. You hit the origin server directly by IP, read a sensitive file it should never expose, scrape the product catalog, and knock on the locked admin panel. Nothing stops you. This is AcmeCorp's "before" picture and the baseline every later chapter is measured against.
AcmeCorp Scenario: Before you deploy a single Cloudflare feature, you need to prove how bad the "before" picture is. Cloudflare is not in front of anything yet, so the origin answers directly on its public IP address (20.88.188.200) on both port 80 and port 443, with a self-signed certificate. In this chapter you play the attacker: confirm the origin is exposed, leak a sensitive file, scrape the catalog, and probe the admin panel, all unimpeded. Record every result. It is the baseline that later chapters must beat.
Run everything from the workstation. Open the Terminal on the in-browser Ubuntu workstation and run the commands there. The workstation reaches the origin over the public internet, exactly as any attacker would. There is no SSH step: the terminal is already on the workstation.

1.1 Confirm the Origin Is Exposed

From the workstation, request the origin directly by IP and read the response headers:

curl -sI http://20.88.188.200/ | head -n 6
Why: With no proxy in front, the raw origin IP is publicly routable and the response advertises the web server software in the Server header. An attacker learns what to target and can reach it directly, so any protection added later can simply be bypassed by talking to this IP.

Now look at how the origin serves HTTPS:

echo | openssl s_client -connect 20.88.188.200:443 2>/dev/null | openssl x509 -noout -issuer -subject
Verify (exposed): the first command returns HTTP/1.1 200 OK and a Server: header naming the origin's web server. The second shows the certificate's issuer and subject are the same self-signed value, which is why a real browser would throw a certificate warning. The origin is reachable, fingerprinted, and not properly encrypted.

1.2 Leak Sensitive Files from the Origin

Developers accidentally left a source-control artifact on the web root. Request it directly:

curl -s http://20.88.188.200/.git/secrets.txt
Why: There is no Web Application Firewall inspecting Layer 7 traffic, so nothing blocks access to obviously sensitive paths like an exposed .git directory. This is exactly the kind of exposure you will virtually patch with the managed WAF in Lab 9.
Verify (secrets leaked): the request returns 200 and prints credentials, including USERNAME=acmeadmin and PASSWORD=#Super.Secret-5+. A file that should never be public is served straight off the origin.

1.3 Scrape the Product Catalog

Simulate a competitor scraping AcmeCorp's catalog. Hammer a product category page and tally the responses:

for i in $(seq 1 50); do curl -s -o /dev/null -w "%{http_code}\n" http://20.88.188.200/services/luggages/; done | sort | uniq -c
Why: With no bot management or rate limiting, an automated client can pull the entire catalog as fast as it likes. This is the scraping you will challenge in Lab 11 and throttle in Lab 12.
Verify (unlimited scrape): all 50 requests return 200 (the output reads 50 200). Every scrape succeeded; nothing slowed the client down.

1.4 Probe the Locked Admin Panel

Finally, knock on the administrative panel and see how it responds:

curl -s -o /dev/null -w "%{http_code}\n" http://20.88.188.200/admin
Why: The admin panel is protected by HTTP Basic authentication at the origin, so an unauthenticated request returns 401 Unauthorized. That is the origin's own control, not an edge control: the panel is still directly reachable on the public IP, and it is up to you to build a real perimeter around it (which you do with an IP list in Lab 10).
Verify (locked, but reachable): the request returns 401. The leaked credentials from 1.2 do not unlock it (it is a separate, locked page), and there is no login form to brute-force (/login returns 404). What matters for the baseline is that /admin is reachable directly on the raw IP at all.
You just landed a run of attacks against a completely unprotected origin: exposure and fingerprinting, a leaked secrets file, catalog scraping, and a directly reachable admin panel. Keep these results. In this managed lab you cannot seal the shared origin, so Lab 18 revisits this suite as a concept rather than a live re-test, but every intervening chapter closes one of these gaps at the edge.

Lab 2 - DNS Onboarding & Proxy Control

Those attacks landed because the origin IP is public. You put Cloudflare in front by confirming authoritative DNS, proxying the web records so the real origin IP disappears, and learning when to leave a record unproxied (grey cloud).
AcmeCorp Scenario: AcmeCorp's first priority is to place Cloudflare in front of their server to hide the origin IP (20.88.188.200). In the managed lab the zone is already onboarded (nameservers assigned) for you, so you will confirm that Cloudflare is authoritative, create and proxy the web record so it hides the origin, and practice the proxy on/off decision for a legacy protocol.

2.1 Cloudflare as Authoritative DNS

Concept - onboarding is already done for you. In a real deployment you would click Add a site in the dashboard, enter <student-domain>, select the Enterprise plan, let the quick scan import existing records, and finally change the nameservers at your registrar to the pair Cloudflare assigns (for example howard.ns.cloudflare.com and mia.ns.cloudflare.com). That nameserver change is what makes Cloudflare authoritative. In this managed lab the zone is already onboarded on the Enterprise plan (nameservers assigned), so you confirm the nameservers below, then create the DNS records the labs need.
Why the Enterprise plan matters: the advanced WAF, Bot Management, and API Shield features that later labs use are Enterprise capabilities, which is why the provided zone is on that plan.

From the workstation Terminal, confirm the nameservers are Cloudflare's:

dig +short NS <student-domain>
Expected output (two Cloudflare nameservers, names vary):
howard.ns.cloudflare.com.
mia.ns.cloudflare.com.
Why: Seeing Cloudflare nameservers confirms the domain is fully onboarded to the Cloudflare control plane, which is the prerequisite for every proxy and security feature that follows.

The onboarded zone starts with no DNS records. In the dashboard, open DNS > Records and create an A record for the apex (name @, which represents <student-domain>) pointing to the shared origin 20.88.188.200, with Proxy Status set to Proxied (orange cloud).

Why: The apex record is what publishes the AcmeCorp website through Cloudflare. Proxying it (orange cloud) ensures all traffic hits Cloudflare's security edge first, hiding the true origin IP. This is the primary web record the rest of the guide relies on; you add the public-api and ps subdomains later, in the labs that use them.

2.2 Partial (CNAME) Setup for a Stubborn Partner (Concept)

Why this section is a concept: Partial (CNAME) setup means Cloudflare is not your authoritative DNS: you keep your existing DNS provider and only proxy individual hostnames into Cloudflare. Your lab zone is fully onboarded (Full setup) and shared with the class, so you cannot convert it. This chapter walks the workflow conceptually.
AcmeCorp Scenario: AcmeCorp integrated a partner whose portal lives at portal.example.com, hosted at a legacy DNS provider the partner refuses to move off. AcmeCorp cannot take over the partner's nameservers, but still wants Cloudflare's WAF and CDN in front of that one hostname. Partial (CNAME) setup is exactly the tool: Cloudflare protects a specific subdomain without becoming authoritative for the domain.

Prerequisites you would confirm first. The domain must be on a Business or Enterprise plan, it must keep its current authoritative DNS provider, and it must not be a domain where Cloudflare is already authoritative. Only proxyable record types (A, AAAA, CNAME) can be onboarded this way, and the root/apex cannot be proxied in a partial setup, only subdomains.

Convert the zone to CNAME setup. On the domain's Overview page, in the DNS card on the right, you would select Convert to CNAME DNS Setup, then Convert to confirm. This switches the zone type from Full to Partial.

Prove ownership with the verification TXT record. Cloudflare then generates a Verification TXT Record (also found on DNS > Records). You add that exact record at the partner's authoritative DNS provider; Cloudflare polls for it, marks the zone active, and emails a confirmation. The TXT record proves you control the domain and must stay in place for as long as the zone remains partial.

Onboard the hostname. In Cloudflare you create a proxied (orange-cloud) record for the hostname, for example portal. Then at the partner's provider you create a CNAME for that hostname pointing to Cloudflare's target, for example portal.example.com.cdn.cloudflare.net, and remove any old A/AAAA/CNAME for that hostname. That CNAME is what reroutes visitor traffic into Cloudflare's edge while every other record on the domain keeps resolving straight from the partner's DNS.

No hands-on step: do not attempt to convert your lab zone. The flow above is exactly what you would do for a real partner domain hosted at another provider.

2.3 Controlling Traffic with Proxy Status

In Cloudflare DNS > Records, create a new A record named ftp pointing to 20.88.188.200, and set its Proxy Status to DNS only (grey cloud).

Why: Cloudflare's standard proxy only intercepts HTTP/HTTPS (ports 80/443). A legacy protocol such as FTP (port 21) would be dropped if it were proxied. Grey-clouding exposes the origin IP but lets the legacy protocol function, which is the trade-off you are demonstrating.

From the workstation, query that record and compare it to a proxied record:

dig +short ftp.<student-domain>
dig +short <student-domain>
Expected output: the grey-clouded ftp record returns the raw origin IP 20.88.188.200, while the proxied apex returns Cloudflare edge IPs (for example addresses in 104.x / 172.67.x ranges).
Why: The contrast proves the point: a grey-cloud record leaks the origin IP, while an orange-cloud record hides it behind Cloudflare. When you are done, delete the ftp record (or leave it grey) and make sure the apex stays Proxied so the web application remains protected.

Lab 3 - SSL/TLS & Encryption Standards

The browser padlock and modern TLS need to be in place. You turn on edge encryption (Always Use HTTPS and a modern minimum TLS version), inspect the Universal SSL certificate Cloudflare issues for free, and order an advanced certificate for a deep subdomain that Universal SSL cannot cover.
AcmeCorp Scenario: AcmeCorp needs a valid certificate on the public site and modern encryption standards enforced at the edge. You will secure the "first mile" (client to Cloudflare) with Universal SSL and HTTPS settings, understand what full end-to-end encryption would require, and order an Advanced Certificate for a deep payments subdomain.

3.1 Universal SSL, Flexible Mode & Edge Encryption

Go to SSL/TLS > Edge Certificates and confirm the Universal SSL certificate is Active.

Why: Universal SSL provides the free, automated certificate that covers the apex and one level of subdomains (<student-domain> and *.<student-domain>). It is what removes the browser certificate warning for visitors.

For immediate mitigation, go to SSL/TLS > Overview, open Configure encryption mode, select Flexible under Encryption mode, then click Save. (The manual modes appear as radio options beneath "Automatic SSL/TLS (recommended)".)

Why: Flexible mode encrypts traffic from the user to Cloudflare but connects to the origin over HTTP. It provides immediate relief for browser warnings without any server-side change, which is why it is the fastest first step. You then layer stricter edge settings on top of it, so you can see the difference between raw Flexible mode and a hardened edge.

On the same Edge Certificates page, turn Always Use HTTPS On.

Why: This redirects any plaintext http:// request to https:// at the edge, so visitors are never served over an unencrypted connection.

Still on Edge Certificates, set Minimum TLS Version to TLS 1.2.

Why: TLS 1.0 and 1.1 are deprecated and insecure. Enforcing a modern floor is a baseline compliance requirement.

Verify the redirect and the negotiated protocol from the workstation:

curl -sI http://<student-domain>/ | head -n 4
curl -sI https://<student-domain>/ | head -n 1
Verify: the plaintext request returns a 301/308 redirect to the https:// URL (Always Use HTTPS), and the HTTPS request returns 200. The site now presents a valid, Cloudflare-issued certificate to browsers.

3.2 Full (Strict) + Origin Certificate

AcmeCorp Scenario: Security compliance requires strict end-to-end encryption, not just client-to-Cloudflare. That means Cloudflare must encrypt the connection to the origin and mathematically validate the origin's certificate. The first step is generating the certificate the origin would present.

Go to SSL/TLS > Origin Server and click Create Certificate.

Why: A Cloudflare Origin CA certificate is a free, Cloudflare-signed TLS certificate for the backend server. Creating one avoids the cost and hassle of purchasing a third-party certificate for the origin.

Leave the defaults selected: Generate private key and CSR with Cloudflare, RSA (2048) key type, the pre-filled hostnames (*.<student-domain> and <student-domain>), and 15 years validity. Click Create.

Why: The wildcard plus apex covers every hostname on the zone with a single certificate, and letting Cloudflare generate the key and CSR means you do not need OpenSSL on the workstation.

Copy the generated Origin Certificate and Private Key, save each to a file (for example cert.pem and key.pem), then click OK.

Why: Cloudflare does not store the private key, so this is your only opportunity to capture it. In a full deployment you would paste these onto the origin web server.
Verify: the new certificate appears in the Origin Certificates list showing 2 hosts (*.<student-domain>, <student-domain>) with an expiry roughly 15 years out.
Concept, install and switch to Full (Strict): on a self-hosted origin you would now paste cert.pem and key.pem onto the web server, reload it so it serves the new certificate on port 443, then go to SSL/TLS > Overview > Configure and select Full (Strict) so Cloudflare encrypts the backend hop and validates the origin certificate. In this managed lab the origin is a shared machine you cannot log in to (SSH is blocked), so the install and the switch to Full (Strict) are not performed here.

3.3 Advanced Certificates for Subdomains

First, in Cloudflare DNS > Records, create an A record named payments.platform pointing to 20.88.188.200 with Proxy Status set to Proxied (orange cloud).

Why: The payment portal hostname must resolve through Cloudflare before you can order an edge certificate for it. It is a deep subdomain (payments.platform.<student-domain>) that Universal SSL's single-level wildcard (*.<student-domain>) does not cover, which is exactly why an Advanced Certificate is required.

Go to SSL/TLS > Edge Certificates and click Order an advanced certificate. In Certificate Hostnames, type payments.platform and select the autocompleted full hostname payments.platform.<student-domain>. The form pre-fills the apex (<student-domain>) and wildcard (*.<student-domain>); remove both with their Remove buttons so the certificate covers only the payments hostname.

Why: Universal SSL already covers the apex and wildcard, so a dedicated single-hostname certificate for the deep subdomain is all you need here.

Set Certificate Authority to Google Trust Services, leave Certificate validation method on TXT Validation, keep the default Certificate Validity Period, then click Save.

Why: On this full-setup zone Cloudflare performs Domain Control Validation automatically for TXT validation, so you do not have to add any record by hand.
Verify: the new Advanced certificate for payments.platform.<student-domain> appears in the Edge Certificates list and moves from Initializing to Pending Validation (TXT) to Active (usually a few minutes), and the plan line shows 1 of 100 advanced certificates used.
Why: Advanced Certificate Manager issues certificates for deep subdomain hierarchies that Universal SSL cannot reach, and lets you choose the CA and validation method.

Wait for the certificate to activate, then confirm from the workstation that it has taken precedence over Universal SSL for that specific subdomain by inspecting the presented certificate:

echo | openssl s_client -connect payments.platform.<student-domain>:443 -servername payments.platform.<student-domain> 2>/dev/null | openssl x509 -noout -issuer -ext subjectAltName
Verify: the issuer is your chosen CA (for example O = Google Trust Services) and the Subject Alternative Name lists DNS:payments.platform.<student-domain>. That proves the dedicated advanced certificate, not the Universal SSL wildcard, is being served for the deep subdomain.
Why: Cloudflare intelligently serves the most specific certificate available for a requested hostname, so the advanced certificate is presented for the payments subdomain while Universal SSL still covers the rest of the zone.

Lab 4 - Caching Foundations

Traffic spikes are overwhelming the origin. You establish a caching baseline, offload a dynamic path with a cache rule, honor the developers' cache-control headers, and purge a stale cached response on demand.
AcmeCorp Scenario: Traffic spikes are overwhelming the AcmeCorp origin. You must diagnose what is and is not cached, force caching on a path that is served dynamically by default, respect developer cache headers, and clean up a stale cached response during a live change.
How to read cache headers: several steps ask you to check response headers such as cf-cache-status, age, and cache-control. From the workstation Terminal, the quickest way is:
curl -sSI https://<student-domain>/services/luggages/ | grep -iE "cf-cache-status|age|cache-control"
The cf-cache-status value is one of MISS, HIT, BYPASS, EXPIRED, REVALIDATED, or DYNAMIC. The age header only appears on a HIT and reports how long the object has been in Cloudflare's cache.

4.1 Establishing Caching Baselines

From the workstation, inspect the cache status of a product category page:

curl -sSI https://<student-domain>/services/luggages/ | grep -i cf-cache-status
Why: AcmeCorp needs a known baseline before troubleshooting. HTML pages are not cached by default, so Cloudflare treats this path as dynamic content that is fetched from the origin every time.
Verify (baseline): the header reads cf-cache-status: DYNAMIC, and there is no age header. Every request is going all the way to the struggling origin.

4.2 Control caching with a Cache Rule

Go to Caching > Cache Rules and click Create rule. Name it "Cache luggages category". Under When incoming requests match, set Field = URI Path, Operator = equals, Value = /services/luggages/.

Why: Cache Rules let you override the default caching behavior for a specific path so a normally-dynamic page can be served from the edge, taking load off the origin.

Under Then, set Cache eligibility to Eligible for cache. Then set Edge TTL to Ignore cache-control header and use this TTL, and enter 30 seconds. Click Deploy.

Why: "Eligible for cache" makes the dynamic page cacheable, and "Ignore cache-control header and use this TTL" makes the rule authoritative for 30 seconds regardless of what the origin sends. A short 30-second TTL keeps the lab quick to re-test.

From the workstation, request the path twice and watch the status change:

curl -sSI https://<student-domain>/services/luggages/ | grep -i cf-cache-status
curl -sSI https://<student-domain>/services/luggages/ | grep -i cf-cache-status
Verify (offloaded): the first request is a MISS (or DYNAMIC on the very first hit), and the second returns HIT (or REVALIDATED), with an age header present. The Cache Rule successfully offloaded the path from the origin.

4.3 Leverage cache-control directives

Go to Caching > Configuration and confirm Browser Cache TTL is set to Respect Existing Headers.

Why: AcmeCorp's developers want to control cache lifetime from their application code via a Cache-Control header, rather than from dashboard rules. "Respect Existing Headers" tells Cloudflare to defer to the origin's instructions.

Disable the Cache Rule you created in 4.2 (toggle it off on Caching > Cache Rules).

Why: A Cache Rule that ignores cache-control would override the origin's headers. Disabling it lets the developer's Cache-Control directives take effect so you can observe origin-driven caching.

Purge the path, then reload it a couple of times and inspect the headers:

curl -sSI https://<student-domain>/services/luggages/ | grep -iE "cf-cache-status|age|cache-control"
Verify: with the rule off, control returns to the origin. This origin sends no Cache-Control header for the category page, so there is no cache-control line and cf-cache-status falls back to DYNAMIC (Cloudflare will not cache HTML on its own). That confirms the edge is now deferring to the origin rather than a dashboard rule.
Why: With the rule off, Cloudflare honors whatever Cache-Control the origin sends. If the application added a directive such as Cache-Control: max-age=300, the edge would cache the page for that duration and you would see HIT with an age; because this origin sends none, the page stays DYNAMIC. Either way the cache behavior is driven by the application, exactly as the developers intended.

4.4 Troubleshooting a Cached 404

Diagnose a reported problem: a page that was briefly missing is now returning a stale 404 even after the origin was fixed. The fix is a targeted purge.

Why: Cloudflare can cache error responses (including 404) when configured to, so a transient origin error can "stick" at the edge. A Custom Purge for the specific URL clears the stale response immediately, and you can control error caching with a Status Code TTL in a Cache Rule (for example cache 404 for only 10 seconds).

Go to Caching > Configuration, use Custom Purge, choose Purge by URL, and enter the affected URL (for example https://<student-domain>/services/luggages/). Then reload and confirm the fresh response is served.

Why: A custom purge is surgical: it clears exactly the one object without dumping the entire cache, so the rest of the site keeps its performance benefit while the stale response is removed.

Now make the negative-caching behavior explicit and controlled with a Status Code TTL. By default Cloudflare does not cache 404 responses, so a burst of requests to a missing URL (a broken link, or an attacker fuzzing paths) reaches the origin every time. Create a Cache Rule (Caching > Cache Rules > Create rule) that matches a path known to 404, for example the custom filter expression starts_with(http.request.uri.path, "/missing/"):

Why: A Status Code TTL caches responses based on the origin's HTTP status code, independent of the normal Edge TTL. Caching a 404 for even 10 seconds means a flood of requests for a missing URL is answered from the edge instead of hammering the origin, while the short TTL keeps things fresh once that URL exists again.
Verify the cached 404: request the missing URL twice within 10 seconds and watch the cache status:
curl -sSI https://<student-domain>/missing/nope | grep -iE 'HTTP|cf-cache-status'
curl -sSI https://<student-domain>/missing/nope | grep -iE 'HTTP|cf-cache-status'
Both return 404, but the first shows cf-cache-status: MISS and the second cf-cache-status: HIT, proving the negative response is now served from cache. Wait more than 10 seconds and the next request returns to MISS as the TTL expires.

Lab 5 - Advanced Caching (Concept)

One cache rule is not enough to survive a real surge. Conceptually, you would cache full HTML with a safe cookie bypass for logged-in users, normalize marketing URLs with a custom cache key, and turn on tiered caching so the origin sees a fraction of the load.
AcmeCorp Scenario: One cache rule for a single path will not carry AcmeCorp through a real surge. In a full deployment you would cache entire HTML pages while safely bypassing the cache for logged-in sessions, normalize marketing URLs with a custom cache key, and enable tiered caching so upper-tier data centers shield the origin.
Why this lab is a concept here: these advanced caching behaviors depend on application traffic patterns (logged-in sessions, marketing query strings, a static HTML endpoint) that the shared managed origin does not expose in a testable way. The mechanics below are exactly what you would configure on a production zone.

5.1 Cache Everything & Cookie Bypass (Concept)

You would create a Cache Rule for a marketing landing path with Cache eligibility = Eligible for cache so Cloudflare caches the HTML itself, not just static file types. This lets the page survive a massive traffic spike because most requests are answered from the edge.

Caching HTML is dangerous if users can log in, because a cached personalized page could be served to the wrong user. To make it safe, you would add a second rule that bypasses cache when a session cookie is present (for example Cookie contains session_id). Anonymous visitors get the fast cached page; logged-in users always bypass to the origin.

Why: a request carrying the session cookie reports cf-cache-status: DYNAMIC (bypassed), while an anonymous request reports HIT. That split is what proves the security logic works.

5.2 Custom Cache Key (Concept)

Marketing tracking tags (?utm_source=twitter, ?utm_source=email) create unique URLs that each miss the cache and hit the origin. You would define a Custom Cache Key that ignores query strings, so every variant of a URL collapses into one cached object.

Why: consolidating tracking-tag variants into a single cache key drastically raises the Cache Hit Ratio and removes needless origin load, without changing the URLs marketing hands out.

5.3 Tiered Caching (Concept)

You would enable Tiered Cache (Caching > Tiered Cache) and select Smart Tiered Cache Topology. Instead of every global PoP fetching from the origin, lower-tier PoPs ask an upper-tier PoP first.

Why: tiered caching funnels cache misses through a small number of upper-tier data centers, so the origin sees a fraction of the requests and its bandwidth and CPU load drop sharply during a surge.

Lab 6 - Traffic Management & Rules Engine

The business needs edge logic the origin should not have to run. You localize Portuguese visitors, stand up a campaign redirect, add a security response header, switch on Cloudflare's managed security headers, rewrite a retired product URL to its replacement, and see why the Host-override pattern is handled for you here.
AcmeCorp Scenario: You need to manipulate requests and responses before they reach or leave the AcmeCorp server. You will redirect Portuguese visitors to a localized page, bounce a campaign short link to an external URL, add a clickjacking-protection header, enable a bundle of managed security headers, and transparently redirect a discontinued product path, all at the edge without touching origin code.

6.1 Geo-Redirects (Portugal Localization)

Create a Redirect Rule so visitors from Portugal are sent to the localized landing page. Go to Rules > Overview, and in the Redirect Rules section click Create rule.

Why: AcmeCorp is expanding into Europe. Executing geographic redirects at the edge is much faster than running geographic IP lookups on the struggling origin server, and the dynamic expression keeps the visitor on the same host while sending them to the localized /pt path.
Verify: the rule appears enabled in the Redirect Rules list. Because the workstation is not in Portugal, a normal request is a control and is not redirected:
curl -s -o /dev/null -w "%{http_code}\n" https://<student-domain>/
This returns 200 (not a 3xx), confirming the rule is scoped to Portuguese source IPs only. A visitor geolocated to Portugal receives a 302 to the localized page.

6.2 Campaign Redirects

Marketing is running a campaign whose short link (/promo) must bounce visitors to AcmeCorp's LinkedIn page. Create a second Redirect Rule that matches the campaign path and sends it to an external URL.

Why: This simulates a bulk campaign redirect. Doing it via Cloudflare Rules prevents the origin from having to process and return 301/302 responses, and scoping it to the campaign path keeps the rest of the site working. Cloudflare issues the redirect at the edge, so it behaves identically no matter what the origin serves.
Verify: from the workstation, request the campaign path and confirm the edge issues the redirect:
curl -sSI https://<student-domain>/promo | grep -iE 'HTTP|location'
You should see a 301 and a location: header pointing at the LinkedIn URL. When you are done, disable this rule so it does not interfere with later labs.

6.3 Response Header Transform

First, observe a security win Cloudflare gives you for free. The origin advertises its exact software in the Server response header, which attackers use to fingerprint the backend. Inspect the header on your proxied site:

curl -sSI https://<student-domain>/ | grep -i "^server:"
Why: when a request is proxied through Cloudflare, Cloudflare replaces the origin's Server header with its own value (cloudflare). The origin's real software and version never reach the visitor, with zero configuration.

Now go to Rules > Overview, click Create rule and choose Response Header Transform Rule. Name it "Add X-Frame-Options". Apply it to All incoming requests, choose Set static, header name X-Frame-Options, value SAMEORIGIN, then Deploy.

Why: X-Frame-Options: SAMEORIGIN tells browsers to refuse to render the site inside a frame on another origin, which defends against clickjacking. Injecting it at the edge means AcmeCorp gets the protection without changing origin code.
Verify: from the workstation, confirm both the masked server header and your new header appear:
curl -sSI https://<student-domain>/ | grep -iE "^server:|^x-frame-options:"
You should see server: cloudflare and x-frame-options: SAMEORIGIN.

6.4 Managed Transforms (Security Headers)

You just added one header by hand. Cloudflare can also apply a curated set of best-practice header changes with a single click, using Managed Transforms. Go to Rules > Settings > Managed Transforms (or, from Rules > Overview, click Go to Managed Transforms). Under HTTP response headers, enable Remove "X-Powered-By" headers and Add security headers.

Why: Managed Transforms are pre-built, Cloudflare-maintained header changes you toggle instead of authoring a rule. Add security headers injects a standard browser-hardening set in one click: x-content-type-options: nosniff, x-xss-protection: 1; mode=block, x-frame-options: SAMEORIGIN, referrer-policy: same-origin, and expect-ct: max-age=86400, enforce. Remove "X-Powered-By" headers strips another backend fingerprint, the same way Cloudflare already masks Server. (Note: HSTS / Strict-Transport-Security is not part of this set; you enable HSTS separately under SSL/TLS.)
Verify: reload the response headers and confirm the managed security headers now appear and x-powered-by is gone:
curl -sSI https://<student-domain>/ | grep -iE "x-content-type-options|x-frame-options|x-xss-protection|referrer-policy|x-powered-by"
You should see x-content-type-options: nosniff and x-frame-options: SAMEORIGIN, and no x-powered-by line.

6.5 URL Rewrites

AcmeCorp discontinued the "arrows" product line and wants requests for it served the "luggages" content instead, invisibly, without a public redirect. Go to Rules > Overview, and in the URL Rewrite Rules section click Create rule. Name it "Retire arrows path". Choose Custom filter expression and match URI Path equals /services/arrows/. Under Then rewrite the path and/or query, in the Path group choose Rewrite to with type Static. The value field already shows a leading /, so type services/luggages/ without a leading slash (the final path becomes /services/luggages/). Typing /services/luggages/ here would produce a broken //services/luggages/. Leave the Query untouched, then Deploy.

Why: a URL rewrite changes the path the origin receives while the visitor's address bar is unchanged. Unlike a redirect (which sends a 3xx back to the browser), a rewrite is transparent, so old links and bookmarks keep working and the origin serves the replacement content.
Verify: from the workstation, request the retired path and confirm it returns the luggages content with a 200 (not a redirect):
curl -s -o /dev/null -w "%{http_code}\n" https://<student-domain>/services/arrows/
You should get 200, and fetching the page body shows the luggages content served under the arrows URL.

6.6 Host Header Overrides (Concept)

Why this section is a concept: a Host header override rewrites the Host the origin receives so one hostname's traffic is routed to a different origin virtual host. In this managed lab the public API already has its own preconfigured hostname (public-api.<student-domain>) wired straight to the API vhost, so there is no routing mismatch to fix by hand. The pattern below is what you would use on an origin that routes by Host.
AcmeCorp Scenario: on an origin where the API is a separate virtual host selected by the Host header, a request that arrives with the website's Host lands on the website instead of the API. An Origin Rule with a Host Header Override fixes this at the edge.

You would create an Origin Rule (Rules > Origin Rules) that matches the API path (for example starts_with(http.request.uri.path, "/api/")) and, under Host Header, selects Rewrite to and enters public-api.<student-domain>, leaving SNI, DNS Record, and Destination Port set to Preserve. The origin would then route /api/* to the API virtual host, without changing the public URL or touching origin code.

Lab 7 - Load Balancing & Failover (Concept)

A single origin is a single point of failure. Conceptually, you group origins into pools behind a health monitor and configure automatic failover to a backup when the primary goes down.
AcmeCorp Scenario: AcmeCorp wants high availability so a single server outage does not take the site offline. Load Balancing routes traffic to a healthy pool and fails over automatically when a monitor marks a pool unhealthy.
Why this lab is a concept here: the managed lab exposes a single shared origin IP that you do not control, and testing failover requires taking a primary origin offline (stopping its web server) and having a distinct backup origin to fail over to. You cannot do either on the shared origin, so this chapter walks the design conceptually. On a self-hosted, multi-origin environment it is fully hands-on.

7.1 Origin Pools & Health Checks (Concept)

All Load Balancing objects live under Traffic > Load Balancing, and you build bottom-up: monitor first, then pools, then the load balancer. You would create an HTTP monitor (for example Path /, Port 80, expecting 200) that Cloudflare uses to probe each origin from its edge.

You would then create two Origin Pools, each with one endpoint (the primary origin in one pool, the backup origin in the other) and attach the monitor to both.

Why: pools group your infrastructure so the load balancer knows where to route, and attaching a monitor is what lets Cloudflare mark a pool healthy or unhealthy. A pool must read Healthy before you attach it to a load balancer.

7.2 Implementing Failover (Concept)

You would create a load balancer, add the primary pool first and the backup pool second (order is the failover order), set the fallback pool to the backup, and choose Off (Failover) steering so 100% of traffic goes to the first healthy pool.

To prove it, you would take the primary origin's web server offline. Within a health-check cycle the primary pool goes Unhealthy and traffic shifts entirely to the backup pool, then returns to the primary once it recovers.

Why: Off (Failover) steering gives an active-passive posture. Users see no downtime during a server crash because Cloudflare stops sending traffic to the unhealthy pool automatically and resumes when it is healthy again.

Lab 8 - Advanced Health Checks & Steering (Concept)

A server that returns 200 but serves a broken page is worse than one that is clearly down. Conceptually, you build a content-aware health check that catches these "zombie" origins and steer traffic across pools by weight.
AcmeCorp Scenario: a server can answer with HTTP 200 while serving a completely broken page. A content-aware health check detects these "zombie" origins, and weighted steering distributes traffic across pools by a configured ratio.
Why this lab is a concept here: like Lab 7, it depends on multiple origins you control and on being able to break one origin's response body to trigger the check. That is not possible against the shared managed origin, so the mechanics are described rather than performed.

8.1 Advanced Health Checks (Concept)

A basic HTTP monitor only proves the web server answered 200. You would create a monitor that also inspects the response body, setting Response Body to a required substring such as "status":"healthy" under the monitor's advanced settings, then attach it to the primary pool.

If an origin still returns 200 but its /status body no longer contains that substring (a "zombie"), the content-aware monitor marks the pool unhealthy and traffic fails over, even though a basic check would have kept sending users to the broken app.

Why: the response-body substring is a case-insensitive match that must appear in the response. It proves the application behind the web server is actually functioning, not merely that the process is up.

8.2 Weighted Steering (Concept)

You would change the load balancer's steering policy from Off (Failover) to Random and set pool weights (for example primary 0.6, secondary 0.4) so traffic is split roughly 60/40 across pools.

Why: weights let you send a specific proportion of traffic to each pool, which matters when one origin is smaller and cannot handle an even share of load. True geo-steering and dynamic latency (Proximity) steering require a higher Load Balancing tier; weighted Random steering demonstrates the same "influence which pool serves a request" mechanism.

8.3 Session Affinity (Concept)

Weighted Random steering picks a pool per request, so the same visitor can bounce between origins on consecutive clicks, which breaks a stateful flow (a login session or a shopping cart held on one origin). You would fix this with Session Affinity: on the load balancer's Hostname step (below the load balancer description), tick the Session Affinity checkbox. Cloudflare then issues an affinity cookie that pins each client to one endpoint for as long as that endpoint stays healthy.

Why: Session Affinity generates a cookie that records which endpoint a client was assigned to, so every later request from that client returns to the same endpoint. This preserves per-user session state (cart contents, login) that would otherwise be lost when Random steering spreads requests across origins. On a self-hosted multi-origin environment you would prove it by saving the affinity cookie on one request, reusing it across a batch, and confirming every response comes from a single origin.

Lab 9 - WAF Managed Rulesets

Now you start closing the holes from Chapter 1. You deploy Cloudflare's managed rulesets to virtually patch the exposed sensitive file, tune out a false positive on the contact form, and read Security Analytics to tell an attacker from a customer.
AcmeCorp Scenario: In Lab 1 you pulled /.git/secrets.txt straight off the origin. You cannot fix the origin's exposed file immediately, so you will deploy Cloudflare's WAF to "virtually patch" the server: the managed rulesets recognize and block requests for exposed source-control artifacts and Layer 7 attack patterns before they reach the origin.

9.1 Deploy the WAF and understand OWASP

First, from the workstation, confirm the exposure is still live through Cloudflare (not just on the raw IP):

curl -s -o /dev/null -w "%{http_code}\n" https://<student-domain>/.git/secrets.txt
Verify (still exposed): you get 200. With no WAF rules deployed, Cloudflare forwards the request and the origin serves the secrets file.

Go to Security > Security rules. In the Managed rules section, click Deploy managed ruleset and deploy the Cloudflare Managed Ruleset.

Why: The Cloudflare Managed Ruleset is the primary, Cloudflare-maintained defense against common Layer 7 threats and known-bad requests, including attempts to reach exposed .git and other sensitive paths.

Deploy the Cloudflare OWASP Core Ruleset as well. On its configuration page, leave Anomaly Score Threshold at Medium (40) and Paranoia Level at PL1, and deploy it in Log action to start.

Why: OWASP provides industry-standard protection against the Top 10 (SQLi, XSS, and more). It uses anomaly scoring: each matching rule adds points, and it only acts once the total crosses the threshold. Starting in Log lets you observe impact before you block legitimate traffic; PL1 keeps false positives low.

Now retest the exposed file through Cloudflare:

curl -s -o /dev/null -w "%{http_code}\n" https://<student-domain>/.git/secrets.txt
Verify (virtually patched): the request now returns 403 and the Cloudflare block page. The managed ruleset recognized the request for the exposed source-control path and blocked it at the edge, even though the origin would still happily serve it. Go to Security > Analytics > Events and confirm a matching Block event under Managed rules.

9.2 Dealing with False Positives

The contact form at /contact/ accepts free-text that can look like an injection payload, so legitimate submissions can trip the WAF's SQL-injection rules. First reproduce the false positive from the workstation:

curl -s -o /dev/null -w "%{http_code}\n" "https://<student-domain>/contact/?comment=admin%27%20OR%20%271%27%3D%271%27%20--%20"
Verify the false positive: you get 403 and the Cloudflare block page: a legitimate contact-form submission is hard-blocked. In Security > Analytics > Events a Block event appears for path /contact/ under Managed rules; expand it to see the matched rule (SQLi - Comment) in the Cloudflare Managed Ruleset. The Managed Ruleset blocks this pattern on its own default action, before the OWASP ruleset (running in Log) ever scores it.

Create a surgical exception. Go to Security > Security rules, click Create rule > Managed rules (the New managed rules exception page). Name it "Contact form exclusion". Under When incoming requests match set Field = URI Path, Operator = wildcard, Value = /contact/*. Keep Skip all remaining rules. Change Place at from Last to First, then Deploy.

Why the order is critical: a skip exception only skips rules that run after it. The Place at field defaults to Last, which puts the exception below the rulesets, so they run first and the skip appears to do nothing. Placing it First is what makes the skip effective. Reordering can take 15-30 seconds to apply at the edge.
Verify the exception works: re-send the contact-form request a few times (add a unique marker such as &n=1, &n=2 to avoid de-duplication), and send a control request to a different path. In Security > Analytics > Events the /contact/ requests now show Action = Skip while other paths still show managed-rule events. The exception surgically disabled managed rules on the contact path only.

9.3 Reading Analytics & Identifying Threats

Go to Security > Analytics. The Events tab groups firewall events by Action and by Service (for example, Managed rules) and lists the top source IPs, paths, and countries. The Traffic tab shows sampled request logs with Cloudflare's threat classifications.

Why: analyzing the dashboard helps you tell a distributed attack from a single malicious actor, so you can fine-tune your posture.

Use the filters to answer: which rules or services triggered most, and is a single source responsible? Drill into an event to view the Ray ID, User Agent, ASN, and matched rule.

Verify (Security > Analytics > Events): with Group view by: Action you see summary cards: Block (the .git virtual patch plus the blocked contact payload) and Skip (your exception rule). Under Events by service > Managed rules the matched rules are listed by name (for example Version Control - Information Disclosure, SQLi - Comment, and Contact form exclusion). Because all of your test traffic came from the workstation, a single entry under Source IP Addresses accounts for every event, Paths shows /.git/secrets.txt and /contact/, and User Agents shows curl. This "one IP, one user agent" pattern is exactly how you distinguish a single automated actor from a distributed attack.

Lab 10 - WAF Custom Rules

Managed rules are the floor, not the ceiling. You write custom rules to block traffic by geography, challenge suspicious traffic on the contact form, lock the admin panel to a corporate IP list, and skip security for one trusted partner API.
AcmeCorp Scenario: Standard rules are not enough. You must enforce sanctions by blocking geographic regions, add a friction challenge to a frequently-abused form, lock down the administrator panel to corporate IPs, and bypass security for a trusted B2B partner API.

10.1 Geo Blocking + Different Actions

Go to Security > Security rules. In the Custom rules card click Create rule (or use the Create rule menu at the top right and choose Custom rules). Name it "Geo Block".

Why: Custom rules let you build complex logic using Cloudflare's wirefilter expression language.

Click Edit expression (top-right of the "When incoming requests match" box) to switch to the raw expression editor, then paste an expression that matches everything outside the United States and Canada. Under "Then take action" choose Block, leave the response as the default Cloudflare WAF block page (403), and click Deploy:

not (ip.src.country in {"US" "CA"})
Why: This enforces strict regulatory compliance by preventing requests from unauthorized countries. The ip.src.country field is a two-letter ISO country code derived from the visitor's IP.

Create a second, separate custom rule named "Competitor Monitoring". Set the action to Log and use an expression that matches a single country you want to watch, such as China:

ip.src.country eq "CN"
Why: AcmeCorp wants threat intelligence on a competitor's region without actively blocking it yet. The Log action records matching requests in Security Analytics but takes no mitigating action, so evaluation continues to your other rules.
Testing geo rules from a single location: your workstation only has one source country, so you cannot directly send traffic that appears to come "from" another country. To prove the engine works, temporarily edit the rule's expression to match your own country, test, then change it back.

First, from the workstation, confirm that normal traffic is allowed by your Geo Block rule:

curl -s -o /dev/null -w "%{http_code}\n" https://<student-domain>/
Verify (allowed): you should get 200. Because the workstation is not outside US/CA, the Geo Block expression does not match it, so the request passes.

Now open Security > Security rules > Custom rules, edit Geo Block, temporarily change its expression to match your own country (for example ip.src.country eq "US"), click Save, wait about 15 seconds for the edge to update, then re-run the same curl:

Verify (blocked): the same request now returns 403 and the Cloudflare block page. This proves the geo match and the Block action are working. Change the expression back to not (ip.src.country in {"US" "CA"}) and Save; the curl should return 200 again.

Finally, review the results at Security > Analytics > Events.

Verify (events): with Group view by: Action you will see a Block event from your temporary Geo Block test. Expand an event to confirm the matched rule name, the country, and the action.

10.2 Challenge the Contact Form

Go to Security > Security rules. In the Custom rules card click Create rule. Name it "Challenge Contact Form", click Edit expression, and match the contact path:

http.request.uri.path eq "/contact/"

Under Then take action choose Managed Challenge, and Deploy.

Why: A Managed Challenge presents an interstitial that a real browser solves silently but an automated client cannot, so it filters bot form-spam and scripted abuse without blocking real customers. Cloudflare then issues a cf_clearance cookie to a client that passes, so subsequent requests in that session are not re-challenged.
Verify: from the workstation, request the contact path and check the mitigation header. An automated curl client cannot solve the challenge:
curl -s -o /dev/null -w "%{http_code}\n" https://<student-domain>/contact/
curl -s -D - -o /dev/null https://<student-domain>/contact/ | grep -i "cf-mitigated"
You should get 403 and cf-mitigated: challenge. A real browser would be offered the challenge, solve it, and receive a cf_clearance cookie.

10.3 Lock Down the Admin Panel with an IP List

First, find the workstation's public IP by running this on the workstation:

curl -s https://api.ipify.org; echo

Lists are defined at the account level. Go to Manage account > Configurations > Lists, click Create IP list, set the Identifier to corporate_ips (lowercase letters, numbers, and underscores only; it cannot be changed later), and Create. On the next screen add the workstation's public IP and click Add to list.

Why: Lists let you manage sets of IP addresses centrally and reference them in any rule as $corporate_ips, without rewriting the rule when membership changes.

Return to Security > Security rules, create a custom rule named "Allow Corp Admin Only", click Edit expression, and block /admin for anyone not on the corporate list. Set the action to Block and Deploy:

(http.request.uri.path eq "/admin") and not (ip.src in $corporate_ips)
Why: This builds a perimeter around the backend admin panel: only source IPs on the corporate list may reach /admin; everyone else is blocked at the edge. "Block when NOT in the list" is the correct pattern, because the custom-rule Allow action only skips remaining custom rules; it does not block anyone by itself.
How to read the results: the origin protects /admin with HTTP Basic auth, so a request that reaches the origin returns 401. A request blocked by your rule returns 403 with the Cloudflare block page. So 401 means "the WAF let it through" and 403 means "the WAF blocked it".

From the workstation (whose IP is on the list), confirm you are allowed through:

curl -s -o /dev/null -w "%{http_code}\n" https://<student-domain>/admin
Verify (allowed): you get 401. Your IP is in corporate_ips, so the rule does not match and the request reaches the origin's Basic-auth challenge. To prove the block, remove your IP from the list, wait ~15 seconds, and re-run the curl: it now returns 403. Re-add your IP to restore access.

10.4 Skip Security for a Partner API

Go to Security > Security rules, create a custom rule named "Skip Partner API Security", and click Edit expression. Match requests to the partner API host that carry a trusted-partner header:

(http.host eq "public-api.<student-domain>") and any(http.request.headers["x-partner-api"][*] eq "true")
Why: A trusted B2B partner's automated calls trip the managed ruleset and generate false positives. The custom header x-partner-api: true identifies that trusted traffic. HTTP header names are lowercased in expressions, and http.request.headers[...] returns an array, so it is wrapped in any(...[*] eq "true").

Under Then take action choose Skip. In "WAF components to skip" tick All managed rules, leave Log matching requests enabled, and Deploy.

Why: The Skip action disables the selected security engines for matching requests only, while leaving DDoS protection and everything else active for the rest of your traffic.
Verify: from the workstation, send the same request with and without the header and compare in Security > Analytics > Events:
P="q=admin%27%20OR%20%271%27%3D%271%27%20--%20"
curl -s -o /dev/null -H "x-partner-api: true" "https://public-api.<student-domain>/v1/orders?$P"
curl -s -o /dev/null "https://public-api.<student-domain>/v1/orders?$P"
The request with the header appears as a Skip event and produces no Managed rules event; the request without the header still triggers the managed rules. This proves the bypass works only for trusted partner traffic.

Lab 11 - Bot Management

Competitors are scraping AcmeCorp's pricing with automated bots. You challenge low-scoring automated traffic while explicitly allowing known-good internal tooling.
AcmeCorp Scenario: Competitors run automated bots to scrape AcmeCorp's pricing, skewing analytics and stealing intellectual property. You will deploy Bot Management to challenge scrapers while explicitly allowing trusted automation.

11.1 Bot Analytics & Baseline Rule

First confirm Bot Management is on. Go to Security > Settings and find the Bot traffic section: Bot management should read Always active on this Enterprise zone.

Why: Cloudflare's ML scores every request from 1 (almost certainly automated) to 99 (almost certainly human) and exposes it as cf.bot_management.score. That field only carries a meaningful value when Bot Management is active.

Review live bot traffic in Security > Analytics: click Add filter, choose Bot score, and inspect the low-score band plus the Source user agents and Source ASNs lists.

Why: real scrapers cluster at a low bot score and often share a user agent or ASN. Those patterns tell you where to set your threshold and which traffic to exclude later.

Create the rule: Security > Security rules > Custom rules > Create rule. Name it "Bot Management - Baseline", click Edit expression, and paste the expression below. Scope it to the bot-demo path and start the action on Log, then Deploy. (A ready-made Templates option, "Mitigate definite bot traffic", is also available under Security rules if you prefer a guided starting point.)

(cf.bot_management.score lt 30) and not cf.bot_management.verified_bot and http.request.uri.path contains "/hellobots"
Why: Starting in Log lets you confirm the rule matches the right traffic before challenging anyone. not cf.bot_management.verified_bot excludes verified crawlers such as Googlebot, so SEO is never harmed. Scoping to /hellobots keeps the test focused on the lab's bot-demo path.

Once the Log events look right, edit the rule, change the action to Managed Challenge, and Deploy. Test from the workstation (a single-egress curl client scores as automated, so it is your stand-in scraper):

curl -s -o /dev/null -w "%{http_code}\n" "https://<student-domain>/hellobots"
curl -s -D - -o /dev/null "https://<student-domain>/hellobots" | grep -i "cf-mitigated"
Verify: with the action on Managed Challenge, the request returns 403 and the headers include cf-mitigated: challenge. That header is the definitive proof the challenge fired (a real browser would solve it silently and get through). You will also see the rule firing in Security > Analytics > Events.

11.2 Refine BM Rule to Exclude APIs

Legitimate automation also scores low: a nightly internal BI tool identifies itself with the user agent B1-Bot/1.11, and a trusted catalog-sync job hits /services/luggages/. Both would be challenged by the baseline rule. Edit "Bot Management - Baseline", click Edit expression, and replace the expression with:

(cf.bot_management.score lt 30) and not cf.bot_management.verified_bot and not (http.request.uri.path eq "/services/luggages/") and not (http.user_agent eq "B1-Bot/1.11")
Why: the two and not (...) clauses carve trusted automation out of the challenge while every other low-score request is still caught. This is the normal Bot Management tuning loop: start broad, then add precise exclusions for known-good clients.

With the action on Managed Challenge, test all three cases from the workstation:

D=<student-domain>
curl -s -o /dev/null -w "hellobots:       %{http_code}\n" "https://$D/hellobots"
curl -s -o /dev/null -w "luggages:        %{http_code}\n" "https://$D/services/luggages/"
curl -s -o /dev/null -w "B1-Bot UA:       %{http_code}\n" -A "B1-Bot/1.11" "https://$D/hellobots"
Verify: the /hellobots request is still challenged (403), while /services/luggages/ and the B1-Bot/1.11 request pass through (200). The exclusions work without weakening the main defense.
Ordering gotcha (important for Lab 12): a Managed Challenge is a terminating action that runs in the custom-rules phase, before rate limiting. If you leave this rule on Managed Challenge, your curl test traffic to /hellobots is challenged and never reaches the rate limiter. Because Lab 12 uses different paths this rule does not interfere, but if you broaden it, set its action back to Log before continuing.

Lab 12 - Rate Limiting

Attackers pivot to volumetric abuse. You cap requests to a hot product page per IP, then throttle API-style fuzzing by counting the origin's own error responses.
AcmeCorp Scenario: Attackers cannot easily exploit the WAF, so they pivot to hammering endpoints with abusive volumes of traffic. You will configure rate limits to shut that down.

12.1 Rate Limit By IP (Brute Force Protection)

Go to Security > Security rules, scroll to Advanced rate limiting rules, and click Create rule. Name it "Safes Page Rate Limit", click Edit expression, and match the target path:

http.request.uri.path contains "/services/safes/"
Why: A hot page being hammered by a single client is a classic abuse pattern. A rate limit caps how many requests one client can make in a window, so an automated client cannot pound the endpoint.

Under With the same characteristics leave IP selected. Under When rate exceeds set Requests = 5 and Period = 1 minute. Under Then take action choose Block, and set the duration to 1 minute. Click Deploy.

Why: Counting per IP limits one abusive client without affecting everyone else. A legitimate visitor will not request the same page five times in sixty seconds; an automated script will.
Gotcha: the mitigation duration cannot be shorter than the counting period. With a 1-minute period the shortest valid duration is 1 minute. We use 1 minute so the lab resets quickly for re-testing.

Test from the workstation by bursting past the threshold:

D=<student-domain>
for i in $(seq 1 8); do curl -s -o /dev/null -w "req $i -> %{http_code}\n" "https://$D/services/safes/"; done
Verify: the first 5 requests return 200, then requests 6, 7, 8 return 429 (Too Many Requests). The Block engaged once the per-IP threshold was crossed.

12.2 Rate Limit By Origin Response Code

In the same Advanced rate limiting rules section, click Create rule and name it "Search Fuzzing Protection". Click Edit expression and match a search-style path:

http.request.uri.path eq "/services/search"
Why: Attackers fuzz search endpoints with junk queries to find hidden parameters. Those probes overwhelmingly generate 404 responses, which is the signal we count.

Tick Use custom counting expression. In the Increment counter when box click Edit expression and enter the origin-response condition:

http.response.code eq 404
Why: The counter only advances when the origin responds 404. Normal successful lookups (200) never fill the counter, so real users are never limited. Only clients that keep generating "not found" errors trip the rule. This proves Cloudflare can rate-limit on the origin's response, not just the incoming request.

Set Requests = 10, Period = 1 minute. Under Then take action choose Block with duration 1 minute, then Deploy.

Why: Sustained 404-generating traffic is almost always malicious, so a hard Block is appropriate once the threshold is crossed.

Test from the workstation. The nonexistent path returns 404, so every request feeds the counter:

D=<student-domain>
for i in $(seq 1 12); do curl -s -o /dev/null -w "req $i -> %{http_code}\n" "https://$D/services/search?q=$i"; done
Verify: the first 10 requests return 404 (passed to the origin and counted), then requests 11 and 12 return 429. The switch from 404 to 429 proves the counter advanced only on the origin's 404 responses and the Block engaged at the threshold.

Lab 13 - API Discovery & Endpoint Management

You cannot protect an API you cannot see. You confirm the session identifier, generate traffic so API Shield can discover the real API surface (including an undocumented shadow endpoint), and bring those operations under management.
AcmeCorp Scenario: AcmeCorp's public API (public-api.<student-domain>) is an orders service. Its documented surface includes GET /status and GET / POST /v1/orders, but developers also left an undocumented "shadow" endpoint, GET /internal/accessLogs, exposed. You will discover the true footprint and bring the operations under management.

13.1 Session Identifiers, Discovery & Endpoint Management

Confirm the session identifier. Go to Security > Settings, open the API abuse category, and click into Session identifiers. This managed lab preconfigures a Header identifier named session-id. If it is not present, click Add identifiers, set Type = Header and Name = session-id, and Save.

Why: API Shield uses a per-client identifier (a header, cookie, or JWT claim unique per user) to attribute requests to a session. Without it, Discovery is weaker, and Sequence Analytics and per-endpoint rate-limit recommendations cannot be generated.

Generate API traffic from the workstation. Send a distinct session-id per simulated user across several endpoints, including the undocumented shadow endpoint:

H=public-api.<student-domain>
for u in $(seq 1 20); do
  S="session-id: user-$u"
  curl -s -o /dev/null -H "$S" "https://$H/status"
  curl -s -o /dev/null -H "$S" "https://$H/v1/orders"
  curl -s -o /dev/null -H "$S" -X POST -H "Content-Type: application/json" -d "{\"createdBy\":\"user$u\",\"items\":[\"sku-$u\"]}" "https://$H/v1/orders"
  curl -s -o /dev/null -H "$S" "https://$H/internal/accessLogs"
done
Why: Cloudflare maps the API surface from live traffic. Hitting the undocumented endpoint is what lets Discovery surface it as a "shadow API" that no schema or documentation mentions.
Timing: Discovery (and the rate-limit recommendations below, and Sequence Analytics in Lab 14) build from accumulated traffic and can take up to 24 hours to populate. Run the traffic now and revisit the results the next day.

Discovered endpoints appear under Security > Web assets > Operations (Endpoint Management) via Add operations > Select from Discovery. Until Discovery populates, add operations manually: on Add operations use Add custom operations and enter Method, Hostname (public-api.<student-domain>), and Path (for example POST /v1/orders), then Save operations.

Why: Endpoint Management is the inventory of operations Cloudflare actively governs. Only operations listed here can carry a schema, a rate-limit recommendation, or an authentication check.
Verify: the operation appears in Endpoint Management with its method, hostname, and path. After ~24h, confirm the shadow endpoint /internal/accessLogs shows up under Add operations > Select from Discovery, and (optionally) accept its rate-limiting recommendation to govern it.

Lab 14 - API Schema Validation & Sequence Analytics

A managed API still trusts its callers too much. You onboard the orders API, enforce its OpenAPI contract at the edge so malformed order requests are rejected, and learn how sequence analytics would flag calls that arrive out of their normal order.
AcmeCorp Scenario: Discovering and managing the API is only half the job. You will enforce the OpenAPI schema at the edge so malformed order requests never reach the origin, add a fallthrough block for unknown operations, and understand sequence analytics for detecting abnormal call ordering.

14.1 Schema Validation

First, onboard the orders API. In Cloudflare DNS > Records, create an A record named api (the name must be exactly api) pointing to 4.157.169.241, with Proxy Status set to Proxied (orange cloud).

Why: The orders API runs on a separate origin from the website. Creating a proxied api record puts it behind Cloudflare so API Shield can inspect and validate its traffic at api.<student-domain>.

Obtain the OpenAPI schema for the orders API. Use the lab schema generator at schema-generator.labs.cfdata.org to produce an OpenAPI document whose servers URL points at https://api.<student-domain>.

Why: Cloudflare derives the hostname for each imported operation from the schema's servers block. If it points at a placeholder host, the uploaded operations will not match your live traffic and validation never fires.

Go to Security > Web assets > Schema validation, click Upload schema, select the file, review that the endpoints map to api.<student-domain>, and click Add schema and endpoints. Then tick the POST /v1/orders row, click Change action, choose Block, and Set action.

Why: The schema is a strict contract: it defines exactly which methods, paths, and body fields (types, required fields, and whether extra fields are allowed) the API accepts. With Block, any request that violates it is dropped at the edge before it reaches the origin.

Test from the workstation, one compliant request and a few violations:

H=api.<student-domain>
CT="Content-Type: application/json"
curl -s -o /dev/null -w "valid:          %{http_code}\n" -X POST -H "$CT" -d "{\"createdBy\":\"alice\",\"items\":[\"sku1\"]}" "https://$H/v1/orders"
curl -s -o /dev/null -w "extra field:    %{http_code}\n" -X POST -H "$CT" -d "{\"createdBy\":\"alice\",\"items\":[\"sku1\"],\"evil\":\"x\"}" "https://$H/v1/orders"
curl -s -o /dev/null -w "missing items:  %{http_code}\n" -X POST -H "$CT" -d "{\"createdBy\":\"alice\"}" "https://$H/v1/orders"
Verify: the compliant request returns 200/201 (it reaches the origin and creates the order); the violations return 403. The 403 is Cloudflare's schema-validation block at the edge, distinct from the origin's own 400 for bad input, so seeing 403 (not 400) proves the request never reached the backend.

Finally, add a fallthrough block. In Schema validation, use the Fallthrough template / setting to Block requests to the API host that do not match any known operation.

Why: Schema validation only checks operations it knows about. A fallthrough block rejects requests to undefined operations, closing the gap where an attacker probes paths that are not in the schema at all.

14.2 Sequence Analytics (Concept)

Why this section is a concept: Sequence Analytics needs session identifiers plus accumulated real traffic and can take up to 24 hours to populate, so it cannot be demonstrated live within a single lab session. The workflow below is what you would review and act on once data has built up.

With session identifiers configured (Lab 13), you would generate traffic that follows the intended order (for example: check status, list orders, then create an order) plus some out-of-order calls (create an order with no preceding GETs). Sequence Analytics groups requests by session identifier and highlights the common ordered "journeys" through the API.

You would review the results under Security > Web assets > Sequences. When a risky sequence is identified (for example jumping straight to order creation without the normal preceding steps), you would mitigate it with a custom rule that references the API Shield sequence, or gate the sensitive operation behind a required precondition so out-of-order requests are challenged or blocked.

Why: attackers frequently skip straight to a sensitive action. Flagging abnormal call order catches abuse that each individual request, viewed alone, would look legitimate.

Lab 15 - Threat Intel with Managed Lists (Concept)

Some clients are known bad before they ever reach you. Conceptually, you preemptively block anonymizers and open proxies using Cloudflare's continuously updated managed threat-intelligence lists, with zero maintenance.
AcmeCorp Scenario: As a hardening step, AcmeCorp wants to preemptively block known bad actors, shielding the origin from automated attack networks using Cloudflare's threat intelligence.
Why this lab is a concept here: Cloudflare Managed IP Lists (Anonymizers, Open Proxies, and so on) are gated by plan or add-on and are not enabled on the shared managed-lab zone, so the Value dropdown will not offer them. The rule you would build is shown below for a zone where they are available.

15.1 Using Managed Lists (Concept)

You would go to Security > Security rules > Custom rules, create a rule named "Block Malicious IPs by Managed List", and build the match with the expression editor: Field = IP Source Address, Operator = is in list, and select the managed Open Proxies list, then add an Or condition for the Anonymizers list. The Expression Preview reads:

(ip.src in $cf.open_proxies) or (ip.src in $cf.anonymizer)

You would set the action to Block and deploy.

Why: Cloudflare curates and continuously updates lists of actively malicious and anonymizing IP addresses so you do not build or maintain blocklists by hand. The Anonymizers list already includes Tor exit nodes, so there is no separate "Tor" list. This gives a large, zero-maintenance reduction in scanning noise hitting the origin.
Available managed lists (where entitled): Anonymizers, Botnets / Command and Control Servers, Malware, Open Proxies, and VPNs. If a list is missing from the Value dropdown, it is not enabled for the zone.

Lab 16 - Spectrum for TCP/UDP (Concept)

Not everything AcmeCorp runs speaks HTTP. Conceptually, you extend Cloudflare's DDoS protection and origin masking to raw TCP and UDP services such as SSH. This chapter is a walkthrough only; nothing is provisioned.
AcmeCorp Scenario: AcmeCorp's operations team relies on non-HTTP services (SSH to jump hosts, a telemetry backend, an SMTP relay) that sit on raw TCP and UDP ports and are therefore not protected by the WAF, which only understands HTTP. Spectrum extends Cloudflare's DDoS protection, IP masking, and edge acceleration to those arbitrary TCP/UDP applications.
Why this lab is read-only: Spectrum is a paid, contract-gated add-on. On a zone without a Spectrum entitlement the dashboard shows a trial banner ("Spectrum is a paid feature, and your Account Manager will follow up"), and creating an application provisions a billable resource. This lab walks through the configuration conceptually, without provisioning anything in the shared lab environment.

16.1 Protecting TCP/UDP Applications (Concept)

Spectrum lives at (domain) > Spectrum. The page lists any existing applications and offers Create an Application.

Why not the WAF: WAF, rate limiting, and API Shield operate at Layer 7 (HTTP). A service like SSH (TCP 22) or a UDP game server never speaks HTTP, so those tools cannot see or protect it. Spectrum proxies the raw TCP/UDP connection through Cloudflare's edge, hiding the origin IP and absorbing L3/L4 DDoS, while optionally terminating TLS.

The Create an Application dialog collects, in order: the Application Type (protocol carried edge-to-origin, for example TCP or UDP); a Domain subdomain that becomes the public entry point (Cloudflare creates the DNS record); Edge IP Connectivity (IPv4, IPv6, or both); an Edge Port (single port or a range); the Origin (an IP/DNS record, a Load Balancer, or a Virtual Network); and optional Edge TLS Termination, IP Access Rules, and Proxy Protocols.

A representative configuration would front an SSH jump host: Application Type TCP, Domain ssh.<student-domain>, Edge Port 22, Origin the jump host's IP on port 22. Users would connect to ssh.<student-domain> and reach the origin through Cloudflare, with the origin IP never exposed.

No hands-on step: do not create an application in this lab environment. If your account carries a Spectrum entitlement, the flow above is exactly what you would follow.

Lab 17 - Page Shield: Client-Side Protection

The last blind spot is the customer's own browser. You turn on client-side monitoring to inventory every script and connection, spot a malicious script, then use a Content Security Policy allowlist to close the supply-chain gap.
AcmeCorp Scenario: The AcmeCorp site loads JavaScript in customers' browsers. A supply-chain attack (a compromised third-party script or an injected skimmer) executes entirely client-side, where the WAF never sees it. Page Shield gives visibility into every script, connection, and cookie running in the browser, and lets AcmeCorp enforce an allowlist via Content Security Policy (CSP). This lab uses the demo page ps.<student-domain>/contact/received/, which loads several third-party resources including a malicious sample.

17.1 Client-side Resource Monitoring

Enable monitoring. Go to Security > Settings, open the Client side abuse category, and turn on Continuous script monitoring.

Why: This instructs Cloudflare to detect and track the JavaScript, connections, and cookies running on your site so you can spot malicious or unexpected changes. On this Enterprise zone it is available at no additional cost.

Generate traffic by loading the demo page in the workstation browser: https://ps.<student-domain>/contact/received/. This page deliberately loads several third-party scripts and opens several outbound connections, including a malicious sample. Then review the inventory under Security > Web assets > Client-side resources, across its Scripts, Connections, and Cookies tabs.

Why: this is your live inventory of what actually runs in visitors' browsers. From here you can see each script's URL, where it loads from, and when it first and last appeared. Cloudflare flags known-malicious scripts.
Verify: after the page has been loaded and traffic accumulates, the Scripts tab lists the demo page's third-party scripts (for example useinsider.com, assets.api.useinsider.com, www.rtb123.com, jsdelivr.at) alongside the first-party /js/scripts.min.<hash>.js, and a malicious sample cryptomining.testcategory.com/1.js is surfaced and flagged. The Connections tab lists outbound endpoints the page contacts (for example a hookb.in beacon and a cf-malicious-test.domain.example.com endpoint).
Timing: the inventory is built from real browser traffic seen repeatedly over the previous days, so it is not instant. Load the demo page a few times and revisit the tab later.

17.2 Content Security Rules (CSP)

Monitoring tells you what is running; a Content security rule enforces what is allowed to run. Go to Security > Security rules, find Content security rules, and click Create rule. Name it "Page Shield - Script Allowlist".

Why: a content security rule builds a Content Security Policy (CSP) allowlist. Cloudflare injects the CSP header on matching responses, and any resource the browser tries to load that is not on the allowlist is reported or blocked. This is the direct defense against a script that suddenly starts talking to an attacker's domain.

Scope the rule to your site host, and build the allowlist from the legitimate resources you saw in 17.1 (leaving the malicious ones off, so they become violations):

Set Action = Log to start in report-only mode, then Deploy. Once you confirm nothing legitimate is caught, switch the action to Allow to enforce the allowlist.

Why: the allowlist permits the known-good first-party and third-party resources, while anything outside it violates the policy. With this allowlist the injected cryptomining.testcategory.com script and the hookb.in / cf-malicious-test.domain.example.com connections fall outside and are reported as violations. Log emits the report-only CSP header so browsers report violations without blocking; Allow emits the enforcing header.

Confirm the policy is live by checking the response header from the workstation:

curl -s -D - -o /dev/null https://ps.<student-domain>/contact/received/ | grep -i content-security
Verify: in Log mode the response includes a content-security-policy-report-only header listing your allowed script-src and connect-src sources; in Allow mode it is the enforcing content-security-policy header. A resource outside the allowlist is reported (or blocked) accordingly.

End of hands-on labs. Lab 18 recaps and, as a concept, describes sealing the origin.

Lab 18 - Seal & Validate the Perimeter (Concept)

A perimeter only matters if it cannot be bypassed. Conceptually, you seal the origin so it accepts traffic only from Cloudflare, then you would replay every attack from Chapter 1 and watch each one fail. In this managed lab the shared origin cannot be sealed, so this chapter is a design walkthrough and a recap of everything you built.
AcmeCorp Scenario: You have built a full edge perimeter: DNS and proxy, encryption, caching, WAF, bot management, rate limiting, API Shield, and client-side protection. But a perimeter only matters if attackers cannot walk around it. The origin IP is still reachable directly (as you proved in Lab 1), which means every control could be bypassed by talking to the origin instead of Cloudflare. This final chapter explains how you would close that gap and recaps the architecture.
Why this whole lab is a concept here: sealing the origin is an origin-side or network-side change, and in the managed lab the origin is a shared machine you do not control. You cannot install a client certificate on it or change its firewall, so you cannot seal it or run a truthful attack-replay. On a self-hosted origin this chapter is fully hands-on.

18.1 Seal the Origin (Concept)

Proxying through Cloudflare hides the origin IP, but it does not by itself stop someone who already knows the IP (as you did in Lab 1) from connecting directly and skipping every edge control. Closing that gap requires the origin to reject any request that did not come from Cloudflare. There are two standard ways:

Why: every control in Labs 9 to 17 runs at the Cloudflare edge. If a request can reach the origin without passing through the edge, none of those controls apply. Sealing the origin is what turns a set of edge features into an enforceable perimeter.

18.2 Replay the Lab 1 Attacks (Concept)

On a sealed origin, you would rerun the Lab 1 attacks through the Cloudflare hostname (<student-domain>) and against the raw IP, and compare to your baseline:

Reality in this lab: because the shared origin cannot be sealed, the direct-to-IP request from Lab 1 (curl -sI http://20.88.188.200/) still returns 200 here. That is precisely the gap 18.1 closes on a self-hosted origin: your edge controls only defend traffic that goes through Cloudflare, and an unsealed origin can always be reached around them. Everything you can enforce at the edge (Labs 9 to 17) works; the origin lockdown is the one piece the managed environment cannot demonstrate.

18.3 Architecture Recap (Concept)

You took AcmeCorp from wide open to a defended edge. Here is the perimeter you built, layer by layer, and the Lab 1 problem each layer answers.

LayerChaptersWhat it defends
DNS onboarding & proxy2Hides the origin IP so attackers cannot target it directly.
SSL/TLS encryption3Ends plaintext and certificate warnings; Full (Strict) end-to-end encryption on a self-hosted origin.
Caching & performance4, 5Absorbs traffic spikes at the edge so the origin stays up.
Load balancing & health7, 8Removes the single point of failure and routes around broken origins (concept in this lab).
Traffic & rules engine6Runs business and hardening logic at the edge, off the origin.
WAF (managed + custom)9, 10Virtually patches the exposed file and enforces access policy.
Bot management11Challenges scrapers while allowing trusted automation.
Rate limiting12Caps volumetric abuse and API fuzzing.
API Shield13, 14Discovers the API, enforces its schema, and flags abnormal sequences.
Threat intelligence15Preemptively blocks anonymizers and open proxies (concept in this lab).
Page Shield17Watches the browser for supply-chain and skimming attacks.
Sealed origin18Forces all traffic through Cloudflare so nothing above can be bypassed (concept in this lab).
Why this ordering matters: each layer assumes the ones before it. Encryption is meaningless if traffic never reaches Cloudflare (Lab 2), and the WAF is meaningless if the origin can be reached directly (Lab 18). Building outward from routing to encryption to performance to security, then sealing the origin last, is what makes the perimeter hold.

End of Unified Lab Guide (Legacy Edition).