blob: 0d3fdbff15e94281cf183a08031aaa5396ddea6e (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
# Vercel WAF — rate-limiting rules
Coarse, per-IP abuse protection that runs at the edge, complementing the
app-level limiters in `src/lib/Error/rateLimit.ts` (which handle what the WAF
cannot — per-user windows and the per-(IP, badge) click counter).
These rules are **not committable config** — Vercel WAF rules are managed in the
dashboard (or via Terraform / the REST API), not `vercel.json`. This file is the
spec to enter under **Project → Firewall → Configure → + New Rule**.
Caveats from the platform:
- Counters are **per region** — traffic spread across regions can exceed a
single-region limit.
- Counting keys on Hobby/Pro are **IP / JA4 only** (no app data like a badge id;
that is why the click counter is limited app-side instead).
- Window: min **10s**, max **10min** (Hobby/Pro).
- **Hobby allows only 1 rule per project**; Pro allows 40.
- Start each rule's action as **Log** to observe real traffic, then switch to
**Deny (429)** once the thresholds look right.
## Hobby (single rule)
| Field | Value |
| --- | --- |
| Name | `api-mutations-ratelimit` |
| If | `Request Path` starts with `/api/` **AND** `Request Method` is one of `POST`, `PUT`, `DELETE` |
| Then | Rate Limit — Fixed Window |
| Window / Limit | `60s` / `100` requests |
| Key | `IP` |
| Action | `Deny` (429) |
## Pro (targeted rules)
| Name | If | Window / Limit | Key | Action |
| --- | --- | --- | --- | --- |
| `auth-ratelimit` | Path starts with `/api/oauth/` | `60s` / `20` | IP | Deny |
| `mutations-ratelimit` | Path is one of `/api/badges`, `/api/preferences`, `/api/configuration` **or** starts with `/api/notifications/`; Method `POST`/`PUT`/`DELETE` | `60s` / `60` | IP | Deny |
| `graphql-ratelimit` | Path is `/graphql`; Method `POST` | `60s` / `60` | IP | Deny |
Public GET reads and the click counter are left to the CDN cache and the
app-level limiters respectively; add a coarse `/api/*` GET rule only if you see
read abuse.
|