Rate Limiting
LaunchFast includes rate limiting to prevent API abuse. It uses express-rate-limit with the in-memory store, configured in server/index.ts.
Configuration
Rate limiting is applied as Express middleware before requests reach Remix. The default configuration:
const rateLimitDefault = {
windowMs: 60 * 1000, // 1-minute window
max: 1000 * maxMultiple, // 1000 requests per window
standardHeaders: true, // RateLimit-* headers
legacyHeaders: false, // No X-RateLimit-* headers
keyGenerator: (req) => {
return req.get('fly-client-ip') ?? req.ip
},
}The keyGenerator uses Fly's fly-client-ip header, which cannot be spoofed by clients. If you sit behind a CDN like Cloudflare, replace this with the CDN-specific header (e.g. cf-connecting-ip).
Three tiers
LaunchFast defines three rate limit tiers that are applied based on the request path and method:
| Tier | Max/min | Applied to |
|---|---|---|
| Strongest | 10 | POST to auth paths, GET /verify |
| Strong | 100 | All other non-GET/HEAD requests |
| General | 1000 | All other requests |
The strongest tier protects sensitive paths like /login, /signup, /verify, /reset-password, and /admin.
Development and testing
In development and during Playwright tests, limits are multiplied by 10,000 to effectively disable rate limiting. This prevents test failures caused by hitting limits during fast automated runs.
Multi-instance considerations
The in-memory store works well for single instances but does not share state across multiple instances. If you run multiple instances and need shared rate limiting, express-rate-limit has built-in support for externalizing the store to Redis.
Invariant
Never disable rate limiting in production. This is a security invariant.