Skip to content
← All posts
7 min read

Cloudflare free-tier audit: two quotas we were already exceeding

61 checks, 23 live quotas, and a $5 fix for two problems we did not know we had.

Five domains, twelve Workers, one audit. Two Cloudflare quotas were already on fire and we did not know.

One Cloudflare account runs everything we ship: five domains, twelve Workers, five Pages projects, four D1 databases, R2 buckets, queues. All of it on the free plan. All of it added project by project over months, each addition sensible on its own, and none of it ever looked at as one system.

In July we looked at it as one system. Two quotas came back already broken, and neither was the one we would have guessed.

How the numbers were taken

We run the account through cld-flare-maxxing, our open-source control center. It snapshots the full account state, runs 61 heuristic checks against the snapshot, and refuses to change anything without a dry run and an explicit approval.

# read-only token in .cloudflare-maxxing/.env.cloudflare
node "<skill-path>/scripts/cf-maxxing.mjs" refresh
node "<skill-path>/scripts/cf-maxxing.mjs" dashboard

That produced live figures for 23 quotas, pulled through Cloudflare's GraphQL analytics and REST APIs with a read-only token: requests per Worker, CPU percentiles, D1 rows read and written per day, R2 storage and operations, Pages builds, KV, queues, cache hit ratios per zone, plus the security findings from the check catalog. Not estimates. Not last month's dashboard screenshot.

Most of it came back comfortable. R2 at 0.12 percent of storage. KV literally unused. D1 storage at 0.07 percent. CDN traffic unmetered. Then two lines came back on fire.

Fire one: a quota already spent

Pages builds: roughly 691 used against the free plan's 500 per month. One project, our news site, burned 495 of those on its own.

The cause was structural rather than careless. A news site publishes constantly. Every article was a git push, and every push triggered a production build and a preview build. Editorial volume was wired directly into infrastructure burn, so the better the publishing month, the worse the quota.

Then the part that only an API read catches. The project's preview deployments were configured off. The API setting said "none". They built anyway. Cloudflare keeps a second field, a preview branch include list, and ours still said "all branches". When the two disagree, the wildcard wins. Dashboard saves write both fields consistently; raw API updates do not. That single mismatch was costing roughly half the monthly build quota, silently, for months.

Fire two: a Worker past its ceiling

The free plan gives each Worker invocation 10 ms of CPU. One of ours measured 17 to 45 ms at the median and 675 ms at P99. It worked only because traffic was low. Under real load its requests start dying, and no amount of waiting for the first of the month fixes that. This one was an architecture defect with a countdown attached.

Four options, honestly priced

  1. Fix the preview config mismatch: free, minutes, halves the build burn.
  2. Batch article publishing into 3-4 pushes a day: free, but adds hours of publish latency a news site does not want.
  3. Decouple content from builds entirely (articles into D1, rendered by a Worker): the right long-term architecture, days of work.
  4. Pay Cloudflare $5/month for Workers Paid: zero engineering, immediate.

We priced our own time against the subscription and took option 4. Here is what that changed, per month, measured against what we actually use:

ResourceFreePaidMultiplier
Worker CPU per request10ms30s3,000x
D1 rows read150M25B167x
KV writes30k1M33x
D1 rows written3M50M17x
Pages builds5005,00010x
Concurrent builds155x
Workers requests3M10M3.3x
Durable Objectslockedunlockednew

Both fires went out with one purchase. The blown build quota now sits at 14 percent used. The Worker that was failing its CPU budget at the median got 3,000x of headroom. The plan also changes the failure mode itself. A free-tier quota is a prepaid meter: hit zero and the power goes off. A paid quota is a postpaid one: the lights stay on and the overage turns up on the bill in cents per million.

$60 a year. The cheapest alternative that actually fixed both problems was days of refactor work.

Paid plans ship with zero alarms

Here is the uncomfortable half of that postpaid trade. Cloudflare has no spend cap. There is no stop-at-$X switch anywhere in the product, and after you upgrade, exactly zero usage notifications exist by default. A traffic spike, a buggy loop, or a scripted abuser can run the meter in silence.

So the same afternoon, we built the closest thing to a hard limit the platform actually supports, in layers:

  • Usage alarm: a billing usage alert at 8M Workers requests, 80% of the included 10M, so overage is never a surprise. Plus an HTTP DDoS attack alert. Both email us. Both had to be created explicitly through the alerting API.
  • Per-Worker CPU caps: every one of the twelve Workers now has an explicit CPU ceiling (500ms for most, more for the two that legitimately work hard), all set far above measured P99 but far below the plan's 30-second maximum. One runaway Worker can no longer drain the shared monthly CPU pool.
  • Per-IP rate limits: the free plan includes one rate limiting rule per zone. Ours now block any single IP that exceeds 300 requests per 10 seconds. A human never hits it; a spam script hits it in the first second.
  • Managed WAF: Cloudflare's free managed ruleset, deployed on all five zones. Exploit traffic dies at the edge before it reaches anything that bills.
  • TLS baseline: SSL mode Full (Strict) on every zone (one was still on Flexible, meaning edge-to-origin traffic was plaintext), HSTS for a year with subdomains, DNSSEC enabled on the three zones that lacked it.

Every change ran the same way. A script prints a dry-run plan of exactly what will change. Nothing mutates until a separate break-glass token and an explicit commit flag are both present. Every attempt lands in an audit log, and every domain gets verified live afterward. The riskiest change, SSL strict, went last, with an immediate check that all five sites still served.

Where this leaves us

  • Security findings: 30 before, 11 after, in one afternoon.
  • Both quota fires out: builds at 14 percent of the new ceiling, CPU wall gone entirely.
  • Four independent layers now sit between us and a surprise bill: WAF, rate limits, CPU caps, and usage alarms. Any runaway has to get through all four, and the last one's entire job is to not be quiet about it.
  • Total recurring cost: $5/month. Total engineering cost of the protections: one afternoon, and the scripts are reusable on any account.

What we did not fix

Two things, and both are worth saying plainly. The $5 bought headroom, not a fix: publishing is still coupled to the build pipeline, option 3 is still undone, and a busy enough month will walk us back toward the same ceiling with a bigger number on it. And four layers of alarm are still not a spend cap. Every one of them emails a human. If nobody reads the email at 3 a.m., the meter keeps running.

What we would do again

  1. Audit with live numbers, not vibes. We would have guessed "builds" last.
  2. Quota problems come in three kinds: config debt (minutes to fix), architecture defects (days), and capacity trends (watch, do not act). Sort first; the fix type follows.
  3. Sometimes $5 legitimately beats days of engineering. Price your own time honestly.
  4. If a platform bills by the meter, your first act after upgrading is building the alarms it did not give you.
  5. Check both fields. Preview deployments off but still building is the kind of thing only an actual API read catches, and it was costing us half a quota.
Everything above ran through cld-flare-maxxing, open source. Real numbers, one real account, July 2026.
EngineeringCloudflareInfrastructureSecurityOpen sourceCloudflareCloudflareWorkersGattyWorksBuildInPublicCldFlareMaxxingD1CloudCostEdgeComputingServerlessDevOps

Ready to know?

Send what you want checked or built. Fixed scope, price, and date in writing inside 24 hours, or the website or audit fee on your first project is refunded in full.

24 clock hours. Weekends included.
Book a call