Skip to content

Commit 8feaa6d

Browse files
committed
feat(blog): rate limiting a Node.js API across many instances
A recipe for @imqueue/http-protect behind several gateway instances sharing one Redis: the shared counter, what its TTL-based count really measures, resolving the client address safely behind a proxy (the default trusts X-Forwarded-For from anyone, which lets a client dodge the limit, claim a safe network or get a customer banned), the ::ffff: form that defeats safeNetworks, lifting a permanent ban, Retry-After, and failing fast when Redis is down. Measured on http-protect 3.0.3, Express 5 and Redis 8. The security hub description now names it. The search index's tier-2 budget goes from 400 to 440 KB, in both the generator and the check, as its own comment prescribes when it fires: the site was at 398.2 KB before this post and 404.7 KB with it, with tier 1 at 96.2 of 120 KB.
1 parent a11b98e commit 8feaa6d

6 files changed

Lines changed: 470 additions & 4 deletions

File tree

102 KB
Loading

‎scripts/check-search-index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const OUT = path.join(ROOT, EDITION_DIR);
4949
// generator's copy fails the BUILD, this one fails `npm test` against whatever is
5050
// in _site-org, including a build somebody produced before changing the budget.
5151
const BUDGET_GZ: Record<string, number> =
52-
{ 'search-index.json': 120 * 1024, 'search-text.json': 400 * 1024 };
52+
{ 'search-index.json': 120 * 1024, 'search-text.json': 440 * 1024 };
5353

5454
/** One tier-1 record, in the wire shape the ranker reads. See lib/search-corpus.ts. */
5555
interface Record1 {

‎scripts/gen-search-index.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,16 @@ const RANGES = "search-sections.json";
5050
//
5151
// Tier 2 was raised 320 -> 360 KB on 2026-08-28, when a 3.3k-word post took the
5252
// measured size from 313.9 to 321.6 KB, and 360 -> 400 KB on 2026-09-15, when a
53-
// 3.5k-word post took it from 360.9 to 368.3 KB with tier 1 at 89.9 of 120 KB.
53+
// 3.5k-word post took it from 360.9 to 368.3 KB with tier 1 at 89.9 of 120 KB,
54+
// and 400 -> 440 KB on 2026-10-16, when a 2.6k-word post took it from 398.2 to
55+
// 404.7 KB with tier 1 at 96.2 of 120 KB.
5456
// It is not a UX number: the dialog fetches tier 2 only once a query is longer
5557
// than one character and never awaits it, so tier-1 results paint first and the
5658
// list re-ranks when the prose corpus lands.
5759
// The ceiling exists to make growth visible, so keep it close — a budget with
5860
// years of slack in it stops reporting anything. Raise it again when it fires,
5961
// after checking that tier 1 still has room.
60-
const BUDGET_GZ: Record<string, number> = { [TIER1]: 120 * 1024, [TIER2]: 400 * 1024, [RANGES]: 40 * 1024 };
62+
const BUDGET_GZ: Record<string, number> = { [TIER1]: 120 * 1024, [TIER2]: 440 * 1024, [RANGES]: 40 * 1024 };
6163

6264
function gzipSize(text: string): number {
6365
return zlib.gzipSync(Buffer.from(text), { level: 9 }).length;

‎src/_data/blogTopics.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,6 @@
109109
"security": {
110110
"label": "Security",
111111
"title": "Securing Node.js services and their broker",
112-
"description": "Security articles for Node.js service fleets: encrypting the Redis broker connection with TLS, least-privilege Redis ACL users for a message queue, keeping each tenant's rows out of other tenants' queries, and checking a caller's IP against thousands of allowed networks."
112+
"description": "Security articles for Node.js service fleets: encrypting the Redis broker connection with TLS, least-privilege Redis ACL users for a message queue, keeping each tenant's rows out of other tenants' queries, rate limiting an API that runs on many instances, and checking a caller's IP against thousands of allowed networks."
113113
}
114114
}
Lines changed: 35 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)