# Rate limiting on Cloudflare: rules, Workers, 429

> A rate limit that looks fine in the dashboard can count the wrong thing, in one location only, or on the wrong key. WAF rule, Workers binding, 429.

Published 2026-10-28 · https://aiskills402.com/blog/cloudflare-rate-limiting-design

A rate limit is meant to say "no more than this much, from this caller, in this time". On Cloudflare you can build one in three places: a WAF rate limiting rule at the edge, the Workers Rate Limiting binding inside your code, or a counter you write yourself. Each one can be configured so that it exists, passes review and still does not limit the thing you had in mind. The usual causes are a plan that does not offer the option you wanted, a counter that only sees one location, and a key that many unrelated callers share. The sections below take these causes one by one and end with the reply a limited client should receive.

## Start with what your plan can express

The first surprise is in the rule builder itself. Cloudflare lists an availability table for rate limiting rules, and the free column is narrow: the rule expression can use only the path and the verified-bot flag, counting is by IP address only, the counting period is 10 seconds, the block lasts 10 seconds, and the zone gets one rule ([Cloudflare, rate limiting rules](https://developers.cloudflare.com/waf/rate-limiting-rules/)). Higher plans widen each of those rows step by step, so the same sentence of intent ("100 a minute per customer") can be possible on one plan and impossible on another.

Two practical consequences follow. A free-plan rule on a path cannot tell one kind of caller from another, so you cannot exempt a trusted client or separate paid traffic from page views inside that rule. And a very short counting period turns the rule into a burst damper: it can stop a flood within seconds, but it can never express an hourly or daily allowance. If your real requirement is a quota over a long period, the WAF rule is the wrong place for it on a small plan, and the quota belongs in code that you control.

Before designing anything, write down the plan, the tool and the exact sentence you want enforced. If the sentence cannot be written in the options your plan shows, change the sentence or the tool.

## Remember where counters live

Cloudflare's own page warns that these rules will not admit an exact count of requests to your origin: there can be a delay of a few seconds between seeing a request and updating the counters, and the counters are scoped to the data center ([Cloudflare, rate limiting rules](https://developers.cloudflare.com/waf/rate-limiting-rules/)). Treat a rule as a guard rail, not as a meter.

The Workers binding is more explicit still. Its documentation describes the limit as approximate, and says the threshold applies within a single Cloudflare location, so a count in one place does not add up with a count in another ([Cloudflare, Workers Rate Limiting](https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/)). The same page explains that a check reads a count cached nearby and that requests can exceed the configured threshold before updates arrive. The period you can choose is either 10 or 60 seconds, nothing else, per the same page.

What does this mean for design? A figure such as a per-minute allowance per user, written into the binding, is not a worldwide promise; the Cloudflare page ([Workers Rate Limiting](https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/)) calls it approximate and local to a location. Use the binding to damp bursts cheaply, and keep any figure that must be exact, such as a monthly allowance or a spend ceiling, in a shared store with one authoritative counter.

## Choose a key that identifies the caller, not the road

The most expensive mistake is the key. The binding documentation recommends keys that describe a user or a class of user, such as an API key, a user ID or a tenant ID, and advises against IP addresses and locations because many legitimate users can share one ([Cloudflare, Workers Rate Limiting](https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/)). The WAF page makes a similar point about its "IP with NAT support" option: visitors who clear cookies or browse privately share one bucket, and for payment or login endpoints it suggests combining that option with other characteristics ([Cloudflare, rate limiting parameters](https://developers.cloudflare.com/waf/rate-limiting-rules/parameters/)).

Three situations deserve a second look from anyone building an API.

- Callers that are programs running on a shared platform. Our reading, which is general practice and not a statement from the pages above, is that requests from many unrelated customers of a hosting platform can leave from a small pool of addresses. A limit keyed by address then treats all of them as one visitor, and the first noisy one blocks the rest. Key by something the caller proves, such as a token or account, and keep the address for traffic that has not identified itself.
- IPv6 clients. A single household or office is typically given a large block of addresses, not one. A counter keyed by the full address string lets the client change address on every request. The common remedy is to key IPv6 by its network prefix instead. That is general networking practice, not something the cited pages state.
- Per-client limits on a costly action. Limiting each client does nothing if clients are cheap to create. For anything that costs money or cannot be undone, add one overall ceiling that all clients draw from, checked before the action runs.

## Calibrate before you trust a number

A limit you have never seen trip is a guess. The reliable way to test it is to set the threshold absurdly low, send more requests than that, and confirm that some are refused. Then raise the number step by step. Because the counting windows are short and the counters are not instant, give the test a pause of more than one full window between bursts; otherwise the second burst is partly counted against the first and you will read a leftover as a result. Also send the test through the same path real clients use. Fifty requests from one terminal may all land in one location and one instance, which says little about a limit that is counted per location.

## Use the right tool for one bad address

If a single address is misbehaving right now and you can see it, a rate rule is a clumsy answer, since it counts everyone and may catch honest users. An access rule that blocks that address, limited to the zone and taken out once the incident is over, is the narrower tool. This is a recommendation from practice. Use rate limiting for classes of traffic and an explicit block for one known offender.

## Answer with 429 and tell the client when to return

Once a limit trips, the reply matters as much as the limit. RFC 6585 defines the `429` status as the signal for a caller that sent a flood of requests within a short span, says the response should explain the condition, and allows a `Retry-After` header that says how long to wait ([RFC 6585](https://www.rfc-editor.org/rfc/rfc6585.txt)). The same RFC notes that a server under heavy load is not required to answer every excess request with a 429, because even that reply costs resources, and dropping connections can be more appropriate.

The `Retry-After` value is either a calendar date or a number of seconds, as defined in the HTTP semantics RFC ([RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.txt)). For machine clients, seconds are the simpler choice: compute the time left in the current window and send it. Without it, an automated client may retry at once or guess (our expectation, not a statement from the RFCs), and the retry traffic you wanted to reduce arrives anyway. The client side of this exchange, meaning how a program should wait and try again, is treated in [a guide to retry and backoff errors](/blog/retry-backoff-mistakes).

The block action of a WAF rule is configured on the rule, so check what a real client receives, from outside, before you assume it is a clean too-many-requests reply with a header.

## A short checklist

1. Name the plan, the tool and the kind of callers.
2. Write the limit as one sentence and check that your tool can express it.
3. Choose a key that identifies the caller; treat the address as the fallback.
4. Put exact or long-period quotas in a shared counter; use rules and the binding for bursts.
5. Add one global ceiling for any costly action.
6. Test with a very low threshold, wait a full window between bursts.
7. Return `429` with `Retry-After` in seconds.

## When this does not apply

This text concerns Cloudflare's rate limiting products as their public pages describe them on the date we read them; plans, fields and periods change, so reread the availability table before relying on any limit named here. It does not cover Enterprise features such as complexity-based counting or custom counting expressions, which change the picture. It does not apply if you use a different CDN or gateway, or if all your callers are on a private network with fixed addresses, where an address key is fine. The remarks about shared platform addresses and IPv6 prefixes are general practice, not quotations from the cited pages, and we did not run new measurements for this guide. A limit is also one layer among others: it slows abuse, but it is not authentication and it does not replace it.

## Checking a setup for these mistakes

For a review of a design before release, our [cf-rate-limit-design](https://aiskills402.com/skills/cf-rate-limit-design) skill reads a pasted rule expression, binding config, handler code or test plan and lists the ways it fails to limit what you intend: fields the free plan refuses, periods your plan lacks, address keys for callers behind one shared address, per-location binding counts mistaken for global ones, a raw IPv6 key, no overall ceiling on a costly action, tests fired before the window clears, a rate rule aimed at a single known offender, and replies missing a wait time. Findings come with a short code, the location in your paste and a suggested change, then one overall verdict. The store itself is at https://aiskills402.com.
