The SmolForge API implements comprehensive rate limiting to protect against abuse and ensure fair resource usage.
The rate limiting middleware uses an in-memory Map with TTL entries for high performance. This approach:
If persistence across Worker restarts is needed in the future, the migrations/0004_rate_limits.sql provides a D1-based schema.
POST /api/auth/registerrefs/heads, refs/tags, or /git/POST /api/repos (excluding issues and PRs)X-Storage-Warning header when exceeded/src/middleware/rateLimit.ts/src/components/http.ts/migrations/0004_rate_limits.sqlrateLimitMiddleware#Main middleware that enforces rate limits. Applied globally to all routes.
app.use('*', rateLimitMiddleware);storageQuotaMiddleware#Secondary middleware that checks storage quota and returns warning headers. Applied to API routes only.
app.use('/api/*', storageQuotaMiddleware);Resolves client IP in order of preference:
cf-connecting-ip (Cloudflare header)x-forwarded-for (proxy header)127.0.0.1All rate-limited responses include:
X-RateLimit-Limit: Maximum requests in current windowX-RateLimit-Remaining: Requests remaining in windowX-RateLimit-Reset: Unix timestamp when limit resetsWhen limit is exceeded (429 response):
Retry-After: Seconds to wait before retryingStatus: 429 Too Many Requests
{
"error": "Too many requests",
"message": "Rate limit exceeded. Try again in 45 seconds.",
"retryAfter": 45
}The following routes are excluded from rate limiting:
/health - Health check endpointIf a user exceeds 500MB of repository storage, they receive a warning header:
X-Storage-Warning: 120% of quota usedRequests are not blocked but the header alerts the client to the situation.
When migrating to D1-based persistence, the storage_quotas table tracks:
Example rate limit scenarios:
for i in {1..65}; do
curl https://api.cloudforge.dev/api/users \
-H "X-Forwarded-For: 192.168.1.100"
done
# 61st+ requests return 429for i in {1..125}; do
curl -X POST https://api.cloudforge.dev/api/repos \
-H "Authorization: Bearer <token>"
done
# 121st+ requests return 429for i in {1..35}; do
git push origin main # This repo's push
done
# 31st+ pushes return 429RateLimitEntry objects with separate tracking for general and git operations