Missing rate limiting on sensitive API routes
Rate limiting controls how often a single user, IP, or API key can hit a route. It is the difference between a single failed login attempt and ten thousand per second. Without it, the cheapest attack on your app is also the most damaging.
#What goes wrong
AI tools rarely add rate limiting by default. Your login route, your AI chat endpoint, your contact form, and your password reset flow all sit open. Attackers can brute-force credentials, exhaust your OpenAI credits, or flood your database with garbage signups.
#Why it matters
An unprotected AI endpoint can cost thousands of dollars in API charges in a single night. An unprotected login route is a brute-force target. An unprotected contact form becomes a spam relay. Rate limiting is the cheapest defense against all of these.
#How Heimdall checks for this
Heimdall finds your middleware and your sensitive API routes (auth, AI, payments, webhooks, contact forms, waitlists) and checks each one for the presence of a rate limiting library or pattern. A rate limiter in one file does not protect another file, so each unprotected route is reported separately.
#How to fix it
Use @upstash/ratelimit with a fixed window or sliding window strategy. Add it in middleware.ts for global routes, or inside the route handler for finer control. Key the limit by user ID for logged-in routes and by IP for public ones. Return a 429 with a Retry-After header when the limit is hit.
Frequently asked questions
Is rate limiting per IP enough?
Where should the rate limit data live?
What limits should I start with?
Run this check on your own repo
Heimdall scans your GitHub repo for this and 16 other issues in under a minute.
