Skip to main content

Tiered Sliding-Window Rate Limiter

Availability, denial-of-service mitigation, and brute-force prevention are enforced across veridex-hitech-web-application through a custom, distributed MongoDB-backed sliding-window rate limiter (src/lib/rate-limit/).

The limiter operates atomically in the database layer, allowing it to scale across multiple container instances and serverless workers without requiring external Redis clusters.


1. Rate Limit Tier Matrix & Policies​

The engine categorizes requests into six distinct operational tiers with tailored thresholds, sliding windows, and resilience policies:

Category NameDefault LimitWindow SizeFail-Closed PolicyTarget Routes & Operations
AUTH_STRICT5 requests15 minutes (900,000 ms)true (Fail-Closed)Sign-in authorization, account registration, password reset token validation, OpenAPI docs login.
FORM_SUBMISSION10 requests5 minutes (300,000 ms)false (Fail-Open)All public forms: Quote drafts, Quote completions, Claims FNOL, Contact Us, Scheduled Calls, Careers.
AUTHENTICATED_MUTATION30 requests1 minute (60,000 ms)false (Fail-Open)Profile updates, user avatar upload/delete, customer referral invitation creation.
EXPENSIVE10 requests1 minute (60,000 ms)false (Fail-Open)Heavy analytical queries, failed CRM lead retries, PDF exports, and admin batch processing.
STANDARD_API100 requests1 minute (60,000 ms)false (Fail-Open)Inbound HMAC partner lead ingestion (/api/leads/*), general interactive dashboard queries.
READ_ONLY200 requests1 minute (60,000 ms)false (Fail-Open)Public cacheable content fetches, FAQ listings, and public signature streaming (/api/signatures/[id]).

1.1 Fail-Closed vs. Fail-Open Resilience Strategy​

  • AUTH_STRICT (Fail-Closed): If the MongoDB cluster experiences an intermittent connection failure during a login attempt, AUTH_STRICT blocks the request. This prevents attackers from executing brute-force dictionary attacks during database service interruptions.
  • Other Tiers (Fail-Open): For form submissions and general browsing, if the rate limiter cannot reach MongoDB, the request is allowed through (allowed: true). This ensures that genuine human customers filing quotes or claims are never turned away by temporary analytics latency.

2. Identity Resolution & Client Detection​

The limiter (getClientIP in src/lib/rate-limit/limiter.ts) determines client identity using a prioritized hierarchy:

  1. Authenticated User ID: If the user has an active session (session.user.id), the limiter binds the bucket directly to the account identifier, preventing bad actors from evading limits by rotating IP addresses or VPN tunnels.
  2. Reverse Proxy IP Resolution: For anonymous visitors, the engine parses x-forwarded-for, extracting the leftmost untampered client IP (stripping intermediate cloud load balancer proxies), with fallback to x-real-ip.

3. Composite Store Key Architecture​

Keys stored in the RateLimit collection follow a strict, collision-free composite format:

ratelimit:<category>:<identifier_type>:<identifier_value>:<custom_key>

Examples​

  • Authentication Attempt:
    ratelimit:AUTH_STRICT:ip:203.0.113.195:login:marcus.vance@example.com
  • Quote Form Submission:
    ratelimit:FORM_SUBMISSION:ip:198.51.100.42:quote_submission
  • Avatar Modification by Logged-In User:
    ratelimit:AUTHENTICATED_MUTATION:user:66f4a8b1c2e40001a9b8c7d6:avatar_upload

4. Atomic Increment & TTL Expiration​

The rate limiter executes an atomic upsert operation using MongoDB's $inc operator, eliminating race conditions:

// Atomic Rate Limit Evaluation (src/lib/rate-limit/limiter.ts)
const record = await RateLimit.findOneAndUpdate(
{ key: storeKey },
{
$inc: { count: 1 },
$setOnInsert: { resetAt: resetTime },
},
{ upsert: true, new: true }
);

const allowed = record.count <= tier.limit;
const remaining = Math.max(0, tier.limit - record.count);
const retryAfter = allowed ? 0 : Math.ceil((record.resetAt.getTime() - now.getTime()) / 1000);

Automatic TTL Index Eviction​

The RateLimit collection defines a Time-To-Live index on resetAt:

RateLimitSchema.index({ resetAt: 1 }, { expireAfterSeconds: 0 });

Once resetAt elapses, MongoDB's background eviction thread automatically removes the document, eliminating database bloat without requiring scheduled cleanup cron jobs.


5. HTTP 429 Response & User Feedback​

When a client breaches their designated limit:

  • API Routes: Return HTTP 429 with standard headers:
    HTTP/1.1 429 Too Many Requests
    Content-Type: application/json
    Retry-After: 480
    X-RateLimit-Limit: 5
    X-RateLimit-Remaining: 0
    X-RateLimit-Reset: 1729267200
  • Server Actions & UI: Return a localized error message:

    "We've received too many requests from you recently. Please try again in a few minutes."