Rate Limiting
A handful of endpoints — login, checkout, order creation — are both expensive and abuse-prone. The platform defends them at two levels: a broad HTTP throttle, and a precise application-level limiter.
Level 1 — HTTP throttling with Rack::Attack
Rack::Attack sits in the middleware stack and throttles requests before they
ever reach a controller. It is configured entirely through environment
variables, so limits can be tuned per environment without a deploy:
RACK_ATTACK_REQ_IP_LIMIT=300 # general requests per IP
RACK_ATTACK_REQ_IP_PERIOD=300 # ...per 5 minutes
RACK_ATTACK_LOGIN_LIMIT=10 # login attempts
RACK_ATTACK_LOGIN_PERIOD=60 # ...per minute
RACK_ATTACK_FAILED_LOGIN_LIMIT=5 # failed logins before lockout
RACK_ATTACK_FAILED_LOGIN_PERIOD=20
RACK_ATTACK_FAILED_LOGIN_TTL=300
Failed-login throttling can vary by account as well as by IP, which blunts credential-stuffing that rotates source addresses. Optional GeoIP blocking can refuse traffic from configured countries.
Crucially, the Rack::Attack cache is fail-open. If the backing store is unavailable, requests are allowed through rather than rejected — a throttling outage must not become a site outage.
Level 2 — application limits with OrderRateLimiter
HTTP throttling is blunt: it counts requests, not meaning. OrderRateLimiter
adds a precise, business-aware limit on order creation — how many orders a
given user may place in a window — independent of how many HTTP requests that
took.
It is backed by Solid Cache (PostgreSQL), not Redis. That is deliberate: order limiting is a correctness control, so it runs on the same durable store as the orders themselves, with no dependency on the cache tier being up.
Why two levels
| Rack::Attack | OrderRateLimiter | |
|---|---|---|
| Sits | In middleware, pre-controller | In the order-creation path |
| Counts | HTTP requests, by IP/account | Domain actions, by user |
| Store | Redis (fail-open) | PostgreSQL (durable) |
| Stops | Floods, brute force, scrapers | Order abuse, retries, gaming |
One catches the cheap, high-volume attacks early; the other enforces a rule that only makes sense in terms of the domain. Together they keep the expensive endpoints available for the shoppers who are using them legitimately.
Key files
| Concern | Files |
|---|---|
| HTTP layer | config/initializers/rack_attack.rb, RACK_ATTACK_* env vars |
| App layer | OrderRateLimiter, EmailSendRateLimiter, RazorpayRateLimiter |
| Geo | GeoIpService |