Launch checklist implementation: - Add Privacy Policy page (/privacy) with GDPR compliance - Add Terms of Service page (/terms) - Add custom 404 and error pages with helpful navigation - Add FAQ page (/faq) covering common questions - Add SEO meta tags with OpenGraph/Twitter cards - Add JSON-LD structured data (Organization, WebSite, SoftwareApplication) - Add sitemap.ts and robots.ts for search engines - Add security headers (HSTS, CSP, X-Frame-Options) in vercel.json - Add security.txt at /.well-known/security.txt - Add API rate limiting (100 req/min default, 20 req/min strict) - Add empty states in tool search for better UX - Update AppHeader with FAQ link - Update AppFooter with Privacy/Terms links - Update biome.json to allow dangerouslySetInnerHTML for JSON-LD in page files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
5.6 KiB
Rate Limiting Implementation Summary
Overview
Added comprehensive rate limiting to all public API endpoints in TPMJS.com to prevent abuse and ensure fair usage.
Files Created
/apps/web/src/lib/rate-limit.ts
Core rate limiting implementation featuring:
- In-memory sliding window algorithm
- IP-based request tracking
- Automatic memory cleanup (prevents leaks)
- Cron job bypass (authenticated with CRON_SECRET)
- Configurable rate limits
- Proper 429 responses with Retry-After headers
Files Modified
Protected Endpoints (Default: 100 req/min)
-
/apps/web/src/app/api/health/route.ts- GET endpoint for health checks
-
/apps/web/src/app/api/tools/route.ts- GET endpoint for listing and filtering tools
-
/apps/web/src/app/api/tools/[...slug]/route.ts- GET endpoint for fetching specific tool/package
- POST endpoint for triggering health checks
-
/apps/web/src/app/api/tools/broken/route.ts- GET endpoint for listing broken tools
-
/apps/web/src/app/api/stats/route.ts- GET endpoint for registry statistics
-
/apps/web/src/app/api/tools/validate/route.ts- POST endpoint for validating tpmjs fields
-
/apps/web/src/app/api/tools/report-health/route.ts- POST endpoint for reporting tool health
Protected Endpoints (Strict: 20 req/min)
/apps/web/src/app/api/tools/search/route.ts- GET endpoint for BM25 search (expensive operation)
- Uses STRICT_RATE_LIMIT for lower threshold
Exempt Endpoints (Cron Jobs)
These endpoints already have CRON_SECRET authentication and bypass rate limiting:
/api/sync/changes- NPM changes feed sync/api/sync/keyword- NPM keyword search sync/api/sync/metrics- Download stats updates
Already Protected
/api/tools/execute/[...slug]- Already has database-backed rate limiting (10 executions/hour)
Implementation Pattern
All protected endpoints follow this pattern:
import { checkRateLimit } from '~/lib/rate-limit';
export async function GET(request: NextRequest) {
// Check rate limit
const rateLimitResponse = checkRateLimit(request);
if (rateLimitResponse) {
return rateLimitResponse;
}
// ... rest of endpoint logic
}
Rate Limit Configurations
DEFAULT_RATE_LIMIT
- Limit: 100 requests per minute
- Use case: Standard read/write operations
- Endpoints: Most public endpoints
STRICT_RATE_LIMIT
- Limit: 20 requests per minute
- Use case: Expensive operations (search, analytics)
- Endpoints:
/api/tools/search
Response Format
When rate limited (HTTP 429):
{
"success": false,
"error": "Rate limit exceeded",
"message": "Too many requests. Please try again in 45 seconds.",
"retryAfter": 45,
"limit": 100,
"window": 60
}
Headers:
Retry-After: Seconds until resetX-RateLimit-Limit: Max requests in windowX-RateLimit-Remaining: Always 0 when rate limitedX-RateLimit-Reset: Unix timestamp of reset time
Key Features
1. IP-Based Tracking
Uses x-forwarded-for header (set by Vercel) to identify clients
2. Sliding Window Algorithm
Tracks individual request timestamps for accurate rate limiting
3. Memory Management
- Automatic cleanup every 5 minutes
- Removes entries older than 1 minute
- Caps store at 10,000 entries
- Evicts oldest 20% when cap exceeded
4. Cron Job Bypass
Automatically bypasses rate limiting for requests with valid CRON_SECRET:
if (env.CRON_SECRET && token === env.CRON_SECRET) {
return null; // Allow cron jobs
}
5. Per-Endpoint Configuration
Different endpoints can use different limits based on operation cost
Testing
Manual Testing
# Test rate limit
for i in {1..105}; do
curl http://localhost:3000/api/health
done
# Should see 429 after request 100
Verify Cron Jobs Work
curl -X POST https://tpmjs.com/api/sync/changes \
-H "Authorization: Bearer $CRON_SECRET"
# Should succeed regardless of rate limits
Architecture Considerations
Serverless-Friendly
- Works with Vercel's serverless architecture
- No external dependencies (Redis, etc.)
- Minimal performance impact
Limitations
- Per-instance state: Each serverless instance has its own rate limit store
- Not globally consistent: Under high load, effective limit may be higher
- Cold start resets: State lost when instance scales down
Production Upgrade Path
For stricter enforcement, consider:
- Upstash Redis: Distributed rate limiting with @upstash/ratelimit
- Vercel KV: Built-in key-value store for rate limit counters
Monitoring
Check rate limit logs:
vercel logs <deployment-url> | grep "Rate limit"
Security
- ✅ Cannot spoof IP headers (Vercel controls them)
- ✅ Server-side enforcement (cannot bypass)
- ✅ Cron jobs properly authenticated
- ✅ Basic DDoS protection (not a substitute for WAF/CDN)
Documentation
See /apps/web/RATE_LIMITING.md for comprehensive documentation including:
- Detailed configuration options
- Troubleshooting guide
- Production upgrade recommendations
- Monitoring and analytics
- Future improvements
Verification
All changes verified:
- ✅ Type-checking passes (
pnpm type-check) - ✅ No new dependencies added
- ✅ Cron jobs unaffected (bypass implemented)
- ✅ Public endpoints protected
- ✅ Proper error responses
- ✅ Memory management implemented
Next Steps
Optional enhancements:
- Add rate limit monitoring/analytics
- Implement per-user rate limits (for authenticated requests)
- Add allowlist for trusted IPs
- Upgrade to distributed rate limiting (Upstash/KV) for high traffic
- Add burst allowance for bursty traffic patterns