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:

TierMax/minApplied to
Strongest10POST to auth paths, GET /verify
Strong100All other non-GET/HEAD requests
General1000All 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.