Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

4.1 The 64 KiB quota: one shared pool, body bytes only

The Fetch §4.6 algorithm verbatim, what it counts (bodies only), when slots release, and the myths — simulated live at the byte level.

Levels: Intermediate ~15 min read

Intermediate

The number every beacon engineer knows is 64 KiB. The number every beacon engineer should actually know is where it lives, what it counts, and when it refills — because all three are widely misdocumented. The limit is not in the Beacon specification at all: it is one step down the stack, in the Fetch standard's HTTP-network-or-cache fetch algorithm, and it applies to every keepalive request a page (or its workers) has in flight, together.

The algorithm, in the spec's own words

When a new request reaches the network layer, Fetch §4.6 sums the body lengths of all not-yet-complete keepalive requests in the request's fetch group, adds the new body's length, and refuses the request if the total exceeds the limit:

"Let inflightKeepaliveBytes be 0. … If the sum of contentLength and inflightKeepaliveBytes is greater than 64 kibibytes, then return a network error." — Fetch, HTTP-network-or-cache fetch

For a beacon, that refusal is exactly what the false return documents: the processing model checks the queueable amount before handing off to Fetch, and declines politely. For a fetch(keepalive) call, the same refusal surfaces later, as a rejected Promise carrying a TypeError. Same pool, same arithmetic, different error channel.

Three properties that follow

  • Body lengths only. The sum counts contentLength and the body lengths of in-flight keepalive requests — nothing else. The URL, the headers, and any preflight exchange never enter this arithmetic. Tutorials that budget 'header overhead' or 'URL encoding' against the 64 KiB are describing engineering headroom, not spec accounting.

  • In-flight, not lifetime. A slot is held only while the request is not done. When a request completes, its bytes are released and the pool refills. A page that sends one 60 KiB beacon per minute never accumulates: each slot evaporates on completion. Nothing is 'reserved' across the page's whole life.

  • One pool, all keepalive traffic. Beacons, fetch(keepalive) calls, and deferred fetches all draw from the same fetch-group pool. The Beacon spec's own note says it plainly: 'All requests with this flag set share the same in-flight quota restrictions that is enforced within the Fetch API.' Your sendBeacon budget is not separate from your keepalive-fetch budget.

One more precision that matters at the boundary: the test is greater than 64 kibibytes — 65,536 bytes exactly fits an empty pool, and 65,537 does not. The spec spells the unit out as "64 kibibytes", a fixed 65,536 bytes, not a marketing-flavored 64,000.

The pool, simulated

The runnable example implements the §4.6 decision as a small pool object — queue, refuse, complete, and watch the accounting move. It demonstrates the boundary case (65,536 in, then one byte refused, then released), the shared-pool case, and the non-facts (URL and headers never counted):

Interactive Runnable example
Edit

Ready to run.

Myths this page exists to kill

Common quota folklore versus the normative text.

Claim

What the spec actually says

“The limit is 256 KiB.”

No 256-KiB number exists anywhere in Beacon or Fetch for this limit. The network error threshold is 64 kibibytes.

“Headers and URL count against it.”

The §4.6 sum uses body lengths only. (The deferred-fetch quota — a different mechanism for fetchLater — does count the whole request; do not confuse the two.)

“Slots are held for the page's lifetime.”

Held only while requests are in flight; released on completion.

“Each beacon gets its own 64 KiB.”

One pool per fetch group, shared by every keepalive request in it.

Quick check

An empty pool. One call queues a body of exactly 65,536 bytes. A second call with a 1-byte body returns…

navigator.sendBeacon(url, 'x'.repeat(65536)); // true
navigator.sendBeacon(url, 'y'); // 

Key takeaways

  • The limit lives in Fetch §4.6, not in the Beacon spec: the sum of a new body plus all in-flight keepalive bodies must not exceed 64 kibibytes
  • Body bytes only — URL, headers, and preflights never count against this pool.
  • One pool per fetch group, shared by sendBeacon, fetch(keepalive), and deferred fetches alike.
  • Slots release on request completion: in-flight accounting, not lifetime accounting.
  • 65,536 fits an empty pool (strictly-greater test); 65,537 does not — verified live in this series' fixture.
Was this page helpful?

Mark this learning resource complete to track your learning progress.