Praktický návod
Jak implementovat rate limiting API
Omez rychlost před drahou prací a dej klientovi jasně vědět, kdy může pokračovat.
Nejdřív stručně
Co rate limit chrání?
Rate limiting omezuje počet requestů za časové období. Chrání API před náhodnou smyčkou, příliš agresivním klientem i částí útoků, ale nenahrazuje autentizaci ani ochranu infrastruktury.
Throttling může požadavky zpomalovat nebo řadit do fronty; tvrdý limit je po vyčerpání odmítne. Sdílený Redis drží počitadlo konzistentní pro všechny běžící instance aplikace.
Připrav si
Co musíš zvolit před implementací
Číslo bez kontextu není politika. Urči, koho, co a v jakém okně omezuješ.
- Identitu klienta: ideálně účet nebo ID API klíče. IP adresa je vhodná jen jako doplňkový limit pro anonymní provoz.
- Rozsah limitu: celý účet, konkrétní endpoint nebo nákladná operace. Přihlášení potřebuje jinou politiku než čtení katalogu.
- Kapacitu a časové okno, například 100 requestů za 60 sekund, odvozené z reálné kapacity a legitimního provozu.
- Sdílený Redis dostupný všem instancím aplikace a rozhodnutí, co se stane při jeho výpadku.
Kroky 1 až 3
Vynucuj limit atomicky a předvídatelně
Začni pevným oknem. Je jednoduché na provoz i vysvětlení; plynulejší token bucket přidej až podle skutečné potřeby.
1. Definuj klíč a pravidla limitu
- Sestav klíč z verze politiky, stabilního ID klienta a názvu skupiny endpointů. Neukládej do klíče celý tajný token.
- Časové okno počítej na serveru. Hodiny klienta ani jeho vlastní hlavičky nesmí rozhodovat o limitu.
- Rate limiter spusť hned po bezpečném určení identity a před databázovým dotazem, externím API nebo jinou drahou prací.
- Zdokumentuj kapacitu i rozsah. Klient musí vědět, zda sdílí limit mezi všemi endpointy a zda se počítají neúspěšné requesty.
composer require predis/predis Oficiální Redis dokumentace k PHP klientům 2. Zvyš počitadlo atomicky v Redisu
- Pro jednoduché pevné okno zvyš INCR a při prvním requestu nastav expiraci. Obě operace proveď jedním Lua skriptem, aby mezi nimi nezůstal klíč bez TTL.
- Skript vrátí aktuální počet a zbývající TTL. Request povol, pokud počet nepřekročil kapacitu.
- Redis klíč nech po konci okna automaticky zaniknout. Nepotřebuješ samostatný úklid počitadel.
- Pevné okno dovolí krátkou špičku na hranici dvou oken. Pokud je to problém, použij sliding window nebo token bucket.
local current = redis.call('INCR', KEYS[1])
if current == 1 then
redis.call('EXPIRE', KEYS[1], ARGV[1])
end
return {current, redis.call('TTL', KEYS[1])} Oficiální Redis návod k rate limitingu 3. Vrať správnou HTTP odpověď
- Po překročení limitu vrať HTTP stav 429 Too Many Requests a krátké chybové tělo ve stejném formátu jako ostatní chyby API.
- Do hlavičky Retry-After dej počet sekund do konce okna. Klient tak nemusí hádat, kdy má request zopakovat.
- Pokud je podporuje tvůj ekosystém, můžeš přidat navrhované RateLimit a RateLimit-Policy hlavičky. Klient pak uvidí kapacitu, zbývající kvótu a čas do obnovení.
- Při výpadku Redisu se vědomě rozhodni mezi fail-open a fail-closed. Přihlášení nebo placená operace může potřebovat přísnější chování než veřejné čtení.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 17
{"error":"rate_limit_exceeded"} Aktuální IETF návrh RateLimit hlaviček pro HTTP Krok 4
Ověř limit i pod souběhem
Sekvenční test neodhalí závod mezi několika PHP procesy. Pošli requesty paralelně a sleduj společné počitadlo.
-
Vyčerpej malý testovací limit
V testovacím prostředí nastav limit na 5. Prvních pět requestů má projít a následující má vrátit 429 s kladným Retry-After.
seq 1 8 | xargs -n1 -P8 -I{} curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8000/api/products -
Ověř izolaci klientů
Vyčerpej limit jedním testovacím účtem a zavolej endpoint jiným. Druhý účet nemá sdílet jeho počitadlo, pokud to politika výslovně neříká.
-
Počkej na obnovení okna
Po uplynutí Retry-After musí další request projít. Zkontroluj také, že starý Redis klíč zmizel a neunikají klíče bez expirace.
redis-cli TTL rate-limit:v1:test-user:catalog
Když to zlobí
Nejčastější chyby
Každá instance počítá requesty zvlášť
Nedrž produkční limit jen v paměti PHP procesu. Všechny instance musí používat stejný Redis a stejný formát klíče.
V Redisu zůstávají klíče bez expirace
INCR a první EXPIRE proveď atomickým Lua skriptem. Sleduj TTL a počet persistentních rate-limit klíčů.
Uživatel za firemní NAT zablokuje ostatní
Po přihlášení limituj podle stabilního ID účtu nebo API klíče. IP limit nech jako širší doplňkovou ochranu, ne jako jedinou identitu.
Klient po 429 posílá ještě více requestů
Retry musí respektovat Retry-After, mít horní mez pokusů a přidat náhodný jitter. Jinak všichni klienti zkusí pokračovat ve stejný okamžik.
Hotovo
API má sdílený a čitelný limit.
Rate limiting teď chrání drahou práci atomicky a klient dostává správnou HTTP odpověď. Limity dál upravuj podle metrik, ne podle odhadu.