Learn / Security / Lesson 07

Size and rate limits

A server with no limits can be knocked over by one request. How caps on body size, request rate and tracked state keep one visitor from taking everyone down.

Last updated: 2026-09-19

What it is

Every input a server accepts has a cost: bytes to read, memory to hold, CPU to parse. Limits put a ceiling on that cost per request and per visitor, so no one client can use it all.

Why it is a rule

How to do it

Cap the body

js
app.use(express.json({ limit: '512kb' }))
app.use(express.urlencoded({ extended: true, limit: '512kb' }))

Pick a limit from what the app really accepts. A file upload route gets its own, larger limit; every other route gets the small one.

Limit the rate, per client

A simple fixed window per IP address is enough for most apps: count requests in the last minute and answer 429 Too Many Requests past the limit, with a Retry-After header.

Bound the limiter itself

A rate limiter that remembers every IP it has ever seen is a memory leak with a public entrance. Cap the number of tracked clients and drop the oldest.

js
if (clients.size >= MAX_TRACKED) clients.delete(clients.keys().next().value)

A Map keeps insertion order, so the first key is the oldest one.

Cap everything else a client can grow

Upload count, open sockets, message size on a websocket, items in a cart, rows in an export. Anything that grows with input needs a ceiling.

How we do it here

Request bodies are capped by default. Every request passes a per-IP limiter that weighs static files lighter than dynamic ones, and the limiter's own memory is capped. Socket messages have a maximum size.

Benefits

Disadvantages

Checklist

Sources

Read this lesson as Markdown