"Just put Cloudflare in front of it" is the default answer for any abuse problem on a Netlify-hosted site. It is not always the right answer. Netlify itself ships a perimeter layer — Application Firewall, Log Drains, real-user Analytics. Cloudflare has overlapping capabilities. Stacking both is often a net negative: cache coherence issues, IP-trust ambiguity, double billing, harder debugging, and an attack surface that grows instead of shrinks. This post walks the decision honestly.
The Serverless Posture Audit flags double-CDN automatically when it detects both cf-ray and x-nf-request-id on the same response — use it to confirm what's actually in front of your site before deciding whether to add another layer.
The protection chain, visualized
Before picking which CDN or WAF goes where, it helps to see the request chain and which layer is responsible for which risk. The diagram below shows the path from the client to your database, and which services you expose drive which layers you actually need to harden.
The red-bordered layers (Origin, Database, Billing, Admin) are where a single miss is catastrophic — a CORS wildcard on the Origin Function lets any site read your authenticated responses; a missing IP allowlist on /admin lets anyone who guesses the path in. The green-bordered layers (CDN, WAF, Edge) are where the highest-leverage-per-dollar defense lives — one Turnstile deploy blocks thousands of form-spam attempts; one Cloudflare rate rule absorbs a botnet hitting your login.
The layers add up: if you expose just static HTML, Netlify's default plus CSP / HSTS is enough. If you expose a public API, you add a rate limit at the Edge Function. If you handle payments, you also add HMAC at the Origin. Stack the specific layers that match your specific exposures — not a blanket "turn everything on" that gives you a false sense of security plus three outages a year from overlapping rules.
What Netlify already ships at the edge
Netlify's own edge layer covers more than most teams realize. As of 2026 the built-ins include:
- Application Firewall — managed rule set (OWASP Core Rule Set derivative) available on Business and Enterprise plans. Blocks SQLi, XSS, command-injection patterns before they reach your functions or origin. Not a bot-management product; it's signature-based.
- Log Drains — push access and function logs to Datadog, New Relic, S3, or an HTTPS endpoint of your choosing. Structured JSON, includes IP, UA, path, status, latency, and function metadata. Available on Pro and above.
- Netlify Analytics — server-side, sampled, no cookies, no JS. Counts requests, bandwidth, 404s, top paths, top referrers. Non-inflated by bots (because it's pre-cache, not from a browser pixel).
- Rate limiting — configurable per-path through the dashboard on Business plans. Token-bucket model, burst + sustained parameters.
- Edge Functions — if you need custom logic before origin, you can run a Deno-based Edge Function that inspects the request, decides, and either proxies or short-circuits.
For a small or mid-sized SaaS, that is often the full perimeter you need. You haven't added a second vendor, a second billing relationship, or a second place where cache rules live.
What Cloudflare adds that Netlify doesn't
Cloudflare's edge is broader in three specific areas:
- Bot Management — machine-learning scored "bot score" per request, applied to rules. Super Bot Fight Mode catches residential-proxy farms Netlify's signature WAF won't. Bot Fight Mode is free; Super is $5/mo per domain on Pro, included on Business and Enterprise.
- Turnstile — free, private, no-CAPTCHA challenge page. Drop-in replacement for hCaptcha and reCAPTCHA on forms. Works even if you're not using Cloudflare as your CDN — you can embed Turnstile on a Netlify-hosted site and verify server-side.
- WAF custom rules — more expressive than Netlify's managed ruleset. You can write a rule like "block if
ip.src.countryis in { country list } ANDhttp.request.uri.pathmatches/api/expensive/" directly in the Cloudflare dashboard. Netlify's Application Firewall doesn't do per-country path-gated rules. - Argo Smart Routing — paid. Picks the fastest path to origin. Marginal gains for most sites; real gains for globally distributed APIs with origin in one region.
- Workers — programmable edge, similar conceptually to Netlify Edge Functions but with a more mature KV / Durable Objects / D1 state story. See the Serverless Posture Audit for the posture patterns each platform requires.
If you genuinely need bot-score-based rules, Turnstile, or deeply custom WAF logic, Cloudflare is the cleaner answer.
The double-CDN problem
Where teams get into trouble is putting Cloudflare in front of Netlify and continuing to use Netlify's edge features. The combination produces specific issues:
Cache coherence
Both Cloudflare and Netlify cache your responses. If your Cache-Control headers say one thing and your Cloudflare page rules say another, the two edges disagree. A stale asset can live in Cloudflare's cache long after Netlify's has invalidated — or vice versa. Debugging a "why is my production site showing the old version?" ticket becomes a tree: is it my browser? CF edge? CF bypass? Netlify edge? Origin? Each layer has its own purge UI.
A practical test: after a deploy, curl -I your homepage and look for both cf-cache-status and x-nf-request-id. If both appear and one of them says HIT when it shouldn't, the two caches are not synchronized. You now have a distributed-systems problem you didn't have before.
IP trust
Netlify's logs show Cloudflare's IP addresses as the client. To get the real client IP, Netlify has to honor the CF-Connecting-IP header (which Netlify does, once configured). But a malicious client can also send a forged CF-Connecting-IP header — and Netlify cannot verify it originated from a real Cloudflare edge unless you configure an IP allowlist matching Cloudflare's published IP ranges. Most teams don't.
This breaks rate limiting in both places. Netlify rate-limiting sees everyone coming from a few Cloudflare IPs and either rate-limits the whole site (false positives) or turns rate limiting off (effectively disabled). Cloudflare rate-limiting still works because it sees the real client, but you now have rate limits only at one layer instead of defense-in-depth.
Double billing on request-count metrics
Cloudflare Workers and Netlify Functions both bill per invocation. If a user request hits Cloudflare, triggers a Worker that proxies to Netlify, triggers a Netlify Function that proxies to origin, that is three billable invocations for one user action. Design around this: Worker-only, Function-only, or static-only where possible. Don't daisy-chain.
Double WAF = double false-positive surface
Two WAFs mean two places a legitimate request can be mistaken for an attack. A request that Cloudflare's WAF approves and Netlify's Application Firewall blocks looks, from the outside, like random 403s. You now debug two rule sets.
Double TLS termination
TLS handshakes at Cloudflare, re-encrypts to Netlify, terminates again, re-encrypts to origin. Three full handshakes per cold connection. Keep-alive amortizes this, but p99 latency on first contact measurably goes up.
When double-CDN is worth it
There are legitimate reasons to stack:
- Pre-deploy DDoS absorption — if you are under active attack, Cloudflare's volumetric absorption is unmatched. Park the apex and
wwwon Cloudflare Free, attack traffic gets absorbed at their edge, origin stays quiet. - Bot-score-gated rate limiting — Cloudflare's bot score plus custom rules is substantially better than Netlify's IP-only rate limiting for abuse detection where the abusers rotate IPs.
- Global Argo routing for a single-region origin — measurable p50 latency improvements for international users hitting a US-only origin.
- Compliance / residency — some Cloudflare WAF rules are simpler to certify against PCI, HIPAA, or similar frameworks than Netlify's equivalents.
If one of those applies to your site, the double-CDN cost is worth paying. But be deliberate. Disable Netlify caching for paths that Cloudflare is caching. Configure Netlify to trust CF-Connecting-IP only from Cloudflare's published IP ranges. Pick one WAF and turn the other's ruleset off.
The simple decision tree
- Do you need bot-score rules or Turnstile? Cloudflare (alone or stacked).
- Do you need per-country path-gated WAF rules? Cloudflare (alone or stacked).
- Do you need structured log drains to Datadog / S3 / a SIEM? Netlify Pro ships this; most Cloudflare plans require Enterprise Logpush.
- Do you need real-user server-side analytics without cookies? Netlify Analytics.
- Do you just need TLS, a CDN, and WAF-lite? Netlify alone; skip the second layer.
- Do you need all of the above? Stack them carefully and accept the complexity tax.
For jwatte.com we chose Netlify-only plus a custom function-level guard (_guard.mjs) because the site is a small static footprint plus 75+ browser-side tools. Adding Cloudflare for bot management didn't clear the complexity bar; the per-function Origin allowlist catches the casual abuse we actually see in the logs.
Spot-checking your own setup
Three quick checks you can run in the next ten minutes:
curl -I https://your-site.com/and read the response headers. Count the fingerprints.cf-ray= Cloudflare.x-nf-request-id= Netlify.x-vercel-id= Vercel.x-amz-cf-id= AWS CloudFront. Two or more? Decide if that's intentional.- Run
/tools/serverless-posture-audit/in Live-probe mode against your primary API endpoint. It flags double-CDN, CORS wildcard, cache-control on dynamic responses, retry-after on 429s, and server version leaks. - Check whether your logs show the real client IP. If every client IP in your access log is a small set of
103.21.244.*or173.245.48.*ranges, you're logging Cloudflare edge IPs instead of real users — a silent rate-limiting killer.
If you're just getting started on the broader question of which web-stack tier you're running — single-site, managed edge, or multi-site — my Kindle book The $97 Launch walks the full decision (stack picks, WCAG compliance, launch budget) in depth.
Fact-check notes and sources
- Netlify Application Firewall, Log Drains, Analytics capabilities and plan tiers: Netlify's feature pricing matrix. Availability and specific rule sets may change between the time of writing and when you read this; verify on the current pricing page.
- Cloudflare Bot Management and Super Bot Fight Mode pricing + availability: Cloudflare pricing and Bot Management product page.
- Cloudflare IP ranges for IP-allowlist configuration: cloudflare.com/ips/.
- MITRE ATT&CK T1557 (Adversary-in-the-Middle) reference: attack.mitre.org/techniques/T1557/.
- OWASP Core Rule Set (the basis for most managed WAF rulesets including Netlify's): coreruleset.org.
- Netlify edge-rate-limiting and edge-functions documentation: docs.netlify.com.
Related reading
- The Serverless Posture Audit — scan your function for Origin allowlist, rate limit, CORS, SSRF, double-CDN, more
- The Six Security Headers Every Site Should Ship in 2026
- Caddy Server Use Cases — when a reverse proxy beats a managed CDN
- Server-side audit probes — why we moved TLS / DNS / HIBP checks off the browser
- Content Protection Playbook — the full stack for indie tool builders
This post is informational, not legal or security-consulting advice. Mentions of Netlify, Cloudflare, Vercel, DigitalOcean, Fly.io, Deno Deploy, AWS, Render, and Railway are nominative fair use. No affiliation is implied.