Overview
What this concept solves
The sliding window counter (also called 'sliding window log approximation' or 'weighted sliding window') fixes the boundary problem of fixed windows without the memory cost of a full log. It's the algorithm Cloudflare uses for most of its rate-limiting product. The idea is clever: keep the simplicity of fixed-window counters, but estimate the rate over a rolling interval by blending the current and previous window's counts.
It's an approximation, not a perfect measurement. But the error is small (typically under 1% for well-distributed traffic), and the memory cost is the same as fixed window: two counters per client.
Mechanics
How it works
The weighted formula
You track two fixed-window counters: the current window's count, and the previous window's. On each request, compute:
estimate = previous_count × (1 − progress) + current_count, where progress = (now − window_start) / window_size
If estimate < limit, allow the request and increment current_count. Otherwise, reject.
Walk through it: at the moment the current window starts (progress = 0), the estimate is previous × 1 + current, giving full weight to the previous window. Halfway through (progress = 0.5), it's previous × 0.5 + current, weighting it half. At the end of the window (progress = 1), it's just current — the previous window is forgotten.
The implicit assumption is that the previous window's traffic was uniformly distributed. This is approximate but holds well enough in practice.
Why this beats fixed window
A burst at the end of the previous window is still in the estimate until the previous window completely 'rolls off' (one full window-size later). So the boundary attack — stacking bursts across a reset — is now blocked: the estimate sees both.
Known approximation error
Because we assume the previous window's traffic was uniform, a tight burst at the very start of the previous window is over-weighted as time goes on, and a burst at the very end is under-weighted. Cloudflare measured this and found <0.003% error for normal traffic distributions.
Interactive prototype
Run it. Break it. Tune it.
Sandboxed simulation embedded right in the page. No setup, no install.
About this simulation
The dashed orange box is a 15-second sliding view across two adjacent fixed windows. The estimate weights the previous window by how much of it still overlaps with 'now'. Try a burst and watch the formula update in real time.
Hands-on
Try these on your own
Open the prototype above, run each experiment, predict the answer, then verify.
Read the formula in motion
Hit Burst of 8 (5 will be allowed, 3 denied — that's the current window). Wait until the status line flashes '↻ Window rolled over': those 5 are now the previous count, and current resets to 0. From here the formula estimate = prev × weight + curr visibly ticks down as the weight slides from 1.00 toward 0.
Boundary attack — does it work?
Try the burst-then-burst trick that broke fixed window. The estimate keeps both bursts in scope until the previous window rolls off completely, so the second burst gets rejected.
Force a rollover
Send a few requests, then wait for the 'Window rolled over' message. Watch the current count become the previous count, and the current resets to 0. The estimate behavior continues smoothly.
In practice
When to use it — and what you give up
When to reach for it
- Public APIs at scale where you want the smoothing of a true sliding window without the memory of a log.
- Edge networks and CDNs where every byte of state matters at multi-million request-per-second scale.
- When you accept a tiny approximation error in exchange for O(1) memory and constant-time operations.
Real-world example
Cloudflare's edge rate limiter uses this exact algorithm. They published the design publicly — it's worth reading for the engineering trade-off discussion.
Pros
- Smooth: no boundary cliff like fixed window.
- O(1) memory — only two counters per client.
- Constant-time decisions; no log scan.
- Distributable: two INCRs against Redis keys, one per window.
Cons
- Approximate — assumes uniform distribution within the previous window.
- More complex to reason about and explain to API consumers than 'X requests per minute, top of the minute'.
- Harder to surface a clean 'resets in X seconds' header (the rate decays continuously).
Reference
Code & further reading
A minimal reference implementation and pointers worth bookmarking.
// Sliding window counter (weighted approximation).
class SlidingWindow {
private windowStart = Date.now();
private currentCount = 0;
private previousCount = 0;
constructor(
private limit: number,
private windowSizeMs: number,
) {}
allow(): boolean {
this.roll();
const progress = (Date.now() - this.windowStart) / this.windowSizeMs;
const estimate = this.previousCount * (1 - progress) + this.currentCount;
if (estimate >= this.limit) return false;
this.currentCount++;
return true;
}
private roll() {
const now = Date.now();
if (now - this.windowStart >= this.windowSizeMs) {
this.previousCount = this.currentCount;
this.currentCount = 0;
this.windowStart = now;
}
}
}
// Redis: two keys per client (prev, current) tied to window IDs.
// INCR is still O(1) — same cost as fixed window.References & further reading
3 sources- Articleblog.cloudflare.com
Cloudflare — How we built rate limiting capable of scaling to millions of domains
Best public description of this algorithm at scale, with accuracy measurements.
- Articlefigma.com
Figma — An alternative approach to rate limiting
Engineering blog on a homegrown Redis-backed limiter — accurate, simple, space-efficient.
- Docsredis.io
Redis — Build rate limiters: fixed window, sliding window, and more
Walks through the two-counter sliding-window-counter pattern with runnable Lua.
Knowledge check
Did the prototype land?
Quick questions, answers revealed on submit. No scoring saved.
question 01 / 03
In the sliding-window formula `estimate = previous × (1 − progress) + current`, what does `progress` represent?
question 02 / 03
Why is the sliding window counter only an approximation?
question 03 / 03
Compared to a sliding-window log, what does the sliding-window counter sacrifice?
0/3 answered
Was this concept helpful?
Tell us what worked, or what to improve. We read every note.