← Back to Blog
Engineering
15 min read

Cloudflare at Portfolio Scale: Managing 13 Zones Without Losing Track

Pro vs Free triage, SSL modes, page rules — our Cloudflare multi-zone playbook.

VDL Platform Team
June 20, 2026
Cloudflare at Portfolio Scale: Managing 13 Zones Without Losing Track

The alert hit Telegram at 4:17am — JustAnalytics was returning 403s to every health check. I pulled up the dashboard, saw green everywhere, and spent twenty confused minutes trying to figure out what was broken before realizing nothing was broken. Cloudflare's Bot Fight Mode had decided our own monitoring daemon was a bot.

Which, technically, it is. But still.

That was the week I finally sat down and systematized how we manage Cloudflare across Velocity Digital Labs. Eight active products — including tools like ClickzProtect for ad fraud detection and VeloCalls for call tracking. Thirteen zones total when you count staging domains and the occasional vanity domain that seemed like a good idea at 2am. (Why did I register that .io? No idea. It redirects now.) One Cloudflare account. A lot of room for things to step on each other if you don't pay attention.

This is the playbook we use now. Not complicated, but the details matter — and I learned most of them by breaking something first.

The Zone Inventory Problem

Here's the thing nobody tells you about running multiple products: Cloudflare zones accumulate. You start with one product, one domain, one zone. Add staging. Add a landing page on a separate domain because SEO reasons. Launch product two. Now you've got four zones. By product four, you're at a dozen zones and you've lost track of which ones are actually doing anything.

Our current inventory:

  • 8 production domains (one per active product)
  • 3 staging domains (not every product needs separate staging DNS)
  • 2 legacy domains we keep around for redirects

That's 13 zones in one Cloudflare account. Each with its own SSL settings, Page Rules, firewall rules, and caching config. Each potentially conflicting with the others if you're not careful. And they don't conflict in obvious ways — they conflict in the "why is this one endpoint returning stale data" ways that take hours to debug.

The first rule: know what you have. I keep a markdown file in our ops repo that lists every zone, what product it serves, whether it's Free or Pro tier, and any non-default settings. When something breaks, that file is the first place I check. This documentation-first approach is something we apply across our entire stack — we wrote about it in building 9 SaaS products: lessons learned.

Should have started this file on day one. Didn't. Created it after zone #7 when I couldn't remember which domain was on which tier.

Free vs Pro: The Triage Decision

Cloudflare Pro is $20/month per zone. Across 13 zones, that's $260/month if you go Pro everywhere. For a bootstrapped studio, that's real money. It adds up faster than you'd expect.

We run 9 zones on Free and 4 on Pro. The decision framework is simple.

Go Pro when:

  • You need more than 3 Page Rules (Free gives you 3, Pro gives you 20)
  • You want real WAF rules beyond Bot Fight Mode basics
  • Analytics granularity matters — Pro gives 72 hours of logs retention vs 24 on Free
  • Image optimization features like Polish and Mirage make sense for image-heavy products
  • The product gets enough traffic that edge caching savings justify the cost

Stay Free when:

  • The product is pre-revenue or barely revenue
  • Traffic is under 100K requests/day
  • You're just using Cloudflare for DNS and SSL, not heavy caching or security features
  • It's a staging domain or redirect-only domain

ClickzProtect is on Pro because it's our highest-traffic product and the WAF rules actually matter for a security-focused tool. JustEmails is on Free because email hosting doesn't need edge caching — the traffic pattern is mostly API calls and dashboard sessions, not cacheable static assets.

The temptation is to put everything on Pro "just in case." Resist it. I burned money on Pro tiers for staging domains for six months before asking myself what, exactly, I was protecting. Audit quarterly. Actually do the audit — don't just say you will.

Page Rules That Don't Step on Each Other

Free tier gets 3 Page Rules per zone. Pro gets 20. Either way, once you have multiple rules, they can conflict — and Cloudflare doesn't warn you.

Rules are evaluated top-to-bottom. First match wins. If Rule 1 matches a URL pattern and Rule 2 would also match, Rule 2 never runs.

Classic mistake we made: a wildcard cache rule for *.productdomain.com/* that inadvertently cached API responses because /api/* matched the wildcard. Users were getting stale data. Took us two hours to figure out because the caching was working, just on things it shouldn't cache.

The fix is explicit ordering and narrow patterns.

Our rule structure for a typical product zone:

  1. Bypass cache for API*productdomain.com/api/* → Cache Level: Bypass
  2. Bypass cache for auth*productdomain.com/auth/* → Cache Level: Bypass
  3. Standard cache for everything else*productdomain.com/* → Cache Level: Standard, Edge TTL: 1 month

The bypass rules go first. They're more specific. The catch-all goes last.

For Pro zones with more complex needs, we add:

  1. Force HTTPS — usually handled at the origin but belt-and-suspenders
  2. Specific page redirects — product renames, old blog URLs, etc.

Every rule gets documented in the zone's markdown file. Before adding a new rule, I check whether it conflicts with existing patterns.

This sounds tedious. It is tedious. It's also the only thing that prevents weird caching bugs at 3am. I wish Cloudflare had a "test this rule against your other rules" feature. They don't. So we document.

SSL Modes: Full Strict or Regret

Cloudflare offers four SSL modes. Only one is correct for most SaaS products.

Flexible: Encrypts browser-to-Cloudflare. Sends plain HTTP to your origin. Never use this. It breaks HTTPS redirect loops, leaks data between Cloudflare and your server, and gives a false sense of security. Cloudflare keeps this option around for legacy reasons (old servers that can't do SSL). Your Railway or Vercel app isn't legacy.

Full: Encrypts both hops but doesn't validate the origin certificate. Better than Flexible, but you're trusting without verifying. An attacker between Cloudflare and your origin could MITM with a self-signed cert.

Full (Strict): Encrypts both hops AND validates the origin certificate. This is the correct mode for anything behind Railway, Vercel, Fly.io, or any modern hosting provider that gives you automatic SSL.

Strict (SSL-Only Origin Pull): Enterprise tier. Ignore unless you're there.

Every zone should be Full (Strict). No exceptions.

If your origin doesn't have a valid cert, fix the origin — don't downgrade Cloudflare. Railway gives you free SSL. Vercel gives you free SSL. There's no excuse for Flexible mode in 2026. None. If someone on your team switches to Flexible "because it's easier," that's a security incident, not a config change.

The one exception: if you're doing something weird with origin IP addresses and can't get cert validation to work, Full without Strict might be a temporary workaround while you fix it. Temporary. Like, a day, not a month.

Bot Fight Mode: The Friendly Fire Problem

Bot Fight Mode is Cloudflare's automated bot detection. It's free, it's one-click, and it sounds great — let Cloudflare handle the bots so you don't have to.

Here's what they don't emphasize: your own automations are bots.

Things Bot Fight Mode has blocked for us:

  • Uptime monitoring scripts
  • CI/CD deploy health checks
  • Our own JustAnalytics collection endpoint when running in dev mode
  • A cron job that fetches public pricing pages for competitive intelligence
  • The Telegram bot that posts deploy notifications

Each of these triggered the "automated traffic" heuristics. Headless browser? Bot. API request without Referer header? Bot. Same IP making 50 requests in a minute? Bot.

The fixes, in order of preference:

1. IP Allowlisting. If your automation runs from a static IP (like a Railway worker or your office), add that IP to a WAF rule that skips Bot Fight Mode. Firewall Rules → Create Rule → If IP equals X, then Allow.

2. API Tokens. For Cloudflare's own API, use API tokens that bypass challenge pages. But this doesn't help with your own products — you'd need to implement token-based bypass in your app.

3. Verified Bot Exceptions. Cloudflare maintains a list of "verified bots" (Googlebot, etc.) that bypass challenges. Your internal tools aren't on this list.

4. Disable Bot Fight Mode. If your product has legitimate bot traffic (an API product, a webhook endpoint, etc.), Bot Fight Mode might not be the right tool. We disable it on products with heavy API usage and handle bot mitigation at the application layer instead.

Honestly? Bot Fight Mode has caused us more debugging sessions than actual bots have. For API-heavy products, I'd skip it entirely.

We've also written about our monitoring setup in the context of running self-healing daemons — Bot Fight Mode was one of the things that broke our monitoring loop before we learned to allowlist.

Caching Strategy Per Product Type

Not every product caches the same way. The mistake is applying a one-size-fits-all caching config.

API-heavy products (JustEmails, JustAnalytics): Minimal edge caching. The value is in the origin logic, not the static assets. We cache fonts and logos. That's about it. API responses are Cache-Control: no-store at the origin, and Cloudflare respects that.

Marketing-heavy products (landing pages, blog sites): Aggressive edge caching. HTML pages cached for hours. Static assets cached for months. This is where Cloudflare actually saves you money — fewer origin hits, lower Railway bills.

Dashboard products (ClickzProtect, JustBrowser): Mixed. Static shell gets cached. API data doesn't. We use stale-while-revalidate for less time-sensitive dashboard widgets.

Webhook receivers (VeloCalls inbound webhooks): Never cache. Webhook requests need to hit your origin every time. An extra Page Rule to bypass cache on /webhook/* paths.

The default Cloudflare behavior is actually pretty reasonable — it respects Cache-Control headers from your origin. The problem is when you want more aggressive caching than your origin specifies, or when you want to override origin headers for specific paths. That's when Page Rules become necessary.

The Zone-Per-Product vs Subdomain Debate

Some studios run everything on subdomains of one root domain: product1.studio.com, product2.studio.com. One zone, simpler management.

We don't do this, and here's why.

Brand separation. Each product should feel like its own thing. clickzprotect.com hits different than clickzprotect.velocitydigitallabs.com. The subdomain approach screams "this is a feature of the parent company." Separate domains say "this is a real product." Users notice. Probably unconsciously, but they notice.

SEO isolation. Domain authority doesn't transfer cleanly across subdomains. If one product gets penalized (spam backlinks, whatever), subdomains might drag each other down. Separate domains contain the blast radius.

Acquisition flexibility. If we ever sell a product, separate domains transfer cleanly. Subdomains require migration.

The VeloCards exception. VeloCards runs its blog at blog.velocards.com rather than /blog on the main domain. Different framework, different deploy, made sense to separate. Still its own zone, but shows that subdomain for specific purposes is fine — we just don't default to it. For more on our infrastructure sharing decisions, check out DevOS where we're building shared tooling for exactly this kind of multi-product orchestration.

The tradeoff: 13 zones instead of 3. More config surface area. More monthly audit work. But the isolation benefits are worth it — and honestly, once you have a system, managing 13 zones isn't that much harder than managing 5. The jump from 1 to 3 is harder than the jump from 3 to 13.

You can also reference our shared-infrastructure post for more on what we share vs isolate.

Automation Without Getting Blocked

We run a lot of automated tooling. Content pipelines, monitoring loops, deploy scripts, health checks. All of it needs to hit Cloudflare-fronted domains without getting challenged.

The pattern that works:

1. Separate automation IPs. Our Railway workers and Mac Mini automation server have known IPs. Those IPs are allowlisted in every production zone's WAF rules.

2. Service-specific User-Agent strings. Our monitoring daemon identifies itself with a custom UA string: VDL-HealthCheck/1.0. We can filter logs by this and, if needed, create WAF rules that treat it differently.

3. CF-Access for internal tools. Some internal dashboards use Cloudflare Access (part of Zero Trust) for auth. That's separate from Bot Fight Mode — Access challenges go through their own flow. But you still need to configure service tokens for automated access to Access-protected endpoints. Yes, that sentence is as confusing as it sounds.

4. Local development bypasses. Our local dev scripts use environment variables to hit origins directly, bypassing Cloudflare entirely. This avoids rate limits and challenges during active development. In production, everything goes through the edge.

The general principle: assume Cloudflare's security features will treat your own tooling as hostile traffic. Design around it.

Monthly Audit Ritual

Cloudflare zones drift. Settings get changed during debugging and never reverted. Page Rules accumulate. IP allowlists include addresses from servers you've long since shut down.

Once a month, I run through:

  • Unused zones: Any domains we're not actually using? Reduce attack surface.
  • Tier appropriateness: Still need Pro on all 4 zones? Still okay with Free on the other 9?
  • Page Rule count: Approaching the limit on any zone? Time to consolidate or upgrade?
  • IP allowlist hygiene: Any IPs that no longer correspond to real infrastructure?
  • SSL mode check: Still Full (Strict) everywhere? Nobody accidentally switched to Flexible during debugging?
  • Caching behavior: Spot-check a few URLs with curl -I to verify headers are what you expect.

This takes about 30 minutes. Boring. Absolutely boring. But necessary. Catches the small drifts before they become production incidents.

I'll admit I skip it some months. Then something breaks and I remember why the ritual exists.

The Config We Actually Run

Across the VDL portfolio, here's the current state:

ProductTierBot FightPage RulesNotes
ClickzProtectProOff (WAF instead)8High traffic, security product
JustAnalyticsProOff5API-heavy, needs bypass rules
JustBrowserFreeOn (IP allowlist)2Dashboard, minimal caching
JustEmailsFreeOff1API product, bots are expected
VeloCardsProOn6Marketing-heavy, needs image optimization
VeloCallsFreeOff2Webhook-heavy
DevOSFreeOn1Early stage, defaults fine
VDL mainProOn4Studio site, blog

The staging domains are all Free with minimal config. Redirect domains have a single Page Rule that 301s to the canonical domain.

Total monthly cost: $80 (4 Pro zones). That's reasonable for the value — SSL, caching, some security, and not having to think about CDN setup per product. Could be $0 if I moved everything to Free. But the Pro zones earn their keep.

Frequently Asked Questions

Should every product domain be on Cloudflare Pro or is Free enough?

Free covers most products. We run 9 zones on Free and 4 on Pro. Pro makes sense when you need more than 3 Page Rules, want Web Application Firewall rules beyond the basics, or have enough traffic that the analytics granularity matters. For a product doing under 100K requests/day, Free is fine. The $20/month per zone adds up fast across a portfolio — we're selective about which zones actually need it.

How do you prevent Page Rules from conflicting across similar product domains?

Naming convention and documentation. Every Page Rule starts with a comment-style prefix in the URL pattern that identifies its purpose — cache rules start with the path they target, redirect rules are explicit. We keep a markdown file per zone listing active rules. Before adding a new rule, check the doc. Cloudflare evaluates rules top-to-bottom and stops at the first match, so order matters. We've had rules silently override each other until we started treating the rule list like code.

What's the right SSL mode for a product behind Railway or Vercel?

Full (Strict) if your origin has a valid certificate, which Railway and Vercel both provide by default. Never use Flexible — it encrypts browser-to-Cloudflare but sends HTTP to your origin, which breaks redirects and leaks data on the backend hop. Full without Strict works but doesn't validate the origin cert, so you're trusting the connection without verifying it. Full (Strict) is the only mode that actually end-to-end encrypts with cert validation.

Why did Bot Fight Mode block our own automation scripts?

Bot Fight Mode uses heuristics to detect automated traffic — headless browsers, API calls without typical browser headers, rapid sequential requests. Our monitoring daemons, health checks, and CI/CD deploys all trigger those heuristics. The fix is IP allowlisting in Cloudflare's WAF rules or using API tokens that bypass the check. We learned this the hard way when Bot Fight Mode started blocking our own uptime monitors and we got paged for a false outage.


Thirteen zones. Four on Pro, nine on Free. $80/month. One markdown file that saves me hours of debugging.

That's the whole system. Nothing fancy. Just documentation and discipline — and I'm better at the documentation part than the discipline part, if I'm being honest.

Velocity Digital Labs →

#cloudflare-zones#cdn-management#multi-product-saas#bot-protection#ssl-configuration#buildinpublic#saasstudio#aiworkforce#buildwithclaude