Skip to content

Troubleshooting

Common issues and how to fix them.

Rate Limit Not Working

Problem: Requests not being rate limited

Symptoms:

  • No 429 responses
  • Headers not appearing
  • Unlimited requests allowed

Solutions:

  1. Check plugin registration:
typescript
// Correct
RedisModule.forRoot({
  clients: { host: 'localhost', port: 6379 },
  plugins: [new RateLimitPlugin()],
})

// Wrong - plugin not registered
RedisModule.forRoot({
  clients: { host: 'localhost', port: 6379 },
})
  1. Check decorator application:
typescript
// Correct
@Get('data')
@RateLimit({ points: 10, duration: 60 })
getData() {}

// Wrong - no decorator
@Get('data')
getData() {}
  1. Check guard is applied:
typescript
// If using decorator, guard is automatic
// If manual, ensure guard is used:
@UseGuards(RateLimitGuard)

Headers Not Appearing

Problem: X-RateLimit-* headers missing

Symptoms:

  • No rate limit headers in response
  • Client can't see remaining requests

Solutions:

  1. Enable headers in config:
typescript
new RateLimitPlugin({
  includeHeaders: true,  // Must be true
})
  1. Check CORS configuration:
typescript
app.enableCors({
  exposedHeaders: [
    'X-RateLimit-Limit',
    'X-RateLimit-Remaining',
    'X-RateLimit-Reset',
    'Retry-After',
  ],
});
  1. Check module-level setting:
typescript
new RateLimitPlugin({
  includeHeaders: true,  // Must be true (default)
})

Redis Connection Issues

Problem: Rate limiting fails with Redis errors

Symptoms:

  • 503 Service Unavailable
  • Errors in logs: "Redis connection refused"
  • All requests rejected (fail-closed)

Solutions:

  1. Check Redis is running:
bash
redis-cli ping
# Should return: PONG
  1. Check connection config:
typescript
RedisModule.forRoot({
  clients: {
    host: 'localhost',  // Correct host?
    port: 6379,         // Correct port?
  },
  plugins: [new RateLimitPlugin()],
})
  1. Use fail-open for availability:
typescript
new RateLimitPlugin({
  errorPolicy: 'fail-open',  // Allow requests on Redis error
})

Key Extraction Issues

Problem: Wrong clients being rate limited together

Symptoms:

  • All users share the same limit
  • Anonymous users hit authenticated limits
  • Rate limits not working per-user

Solutions:

  1. Check key extraction:
typescript
// Wrong - all users share same key
@RateLimit({ key: 'global' })

// Correct - per-user key
@RateLimit({ key: 'user' })
  1. Ensure request.user is set:
typescript
// Auth guard must run BEFORE rate limit guard
@UseGuards(AuthGuard, RateLimitGuard)
@RateLimit({ key: 'user' })
  1. Check IP extraction behind proxy:
typescript
// Trust proxy headers
app.set('trust proxy', true);

// Or custom extractor
new RateLimitPlugin({
  defaultKeyExtractor: (ctx) => {
    const req = ctx.switchToHttp().getRequest();
    return req.headers['x-real-ip'] || req.ip;
  },
})

Algorithm Not Working as Expected

Problem: Rate limiting behavior unexpected

Symptoms:

  • Bursts allowed at window boundaries (Fixed Window)
  • Higher memory usage than expected (Sliding Window)
  • Unpredictable limits (Token Bucket)

Solutions:

  1. Fixed Window burst issue:
typescript
// Problem: 2x limit at window boundary
@RateLimit({ algorithm: 'fixed-window' })

// Solution: Use sliding window
@RateLimit({ algorithm: 'sliding-window' })
  1. Token Bucket configuration:
typescript
// Wrong - too fast refill
@RateLimit({
  algorithm: 'token-bucket',
  points: 100,
  refillRate: 1000,  // 1000 tokens/sec = no limit!
})

// Correct - reasonable refill
@RateLimit({
  algorithm: 'token-bucket',
  points: 100,
  refillRate: 10,  // 10 tokens/sec
})

Performance Issues

Problem: Rate limiting slow

Symptoms:

  • High latency on requests
  • Slow response times
  • Redis CPU high

Solutions:

  1. Check Redis latency:
bash
redis-cli --latency
# Should be < 1ms locally
  1. Use simpler algorithm:
typescript
// Sliding Window uses more memory and operations
@RateLimit({ algorithm: 'sliding-window' })

// Fixed Window is faster
@RateLimit({ algorithm: 'fixed-window' })
  1. Optimize key structure:
typescript
// Long keys waste memory
key: `organization:${org}:user:${user}:endpoint:${endpoint}`

// Shorter keys
key: `${org}:${user}:${endpoint}`

Common Errors

ErrorCauseFix
RateLimitExceededErrorLimit exceededWait for reset or increase limit
RateLimitScriptErrorLua script execution failedCheck Redis connection and version

Debug Mode

Use NestJS logger to debug rate limiting. Set the log level to debug and check the RateLimitGuard output:

typescript
const app = await NestFactory.create(AppModule, {
  logger: ['debug', 'log', 'warn', 'error'],
});

Inspect Redis Keys

bash
# List all rate limit keys
redis-cli --scan --pattern 'rl:*'

# Get specific key (includes algorithm in key)
redis-cli GET rl:sliding-window:192.168.1.1

# Check TTL
redis-cli TTL rl:sliding-window:192.168.1.1

# Delete specific key
redis-cli DEL rl:sliding-window:192.168.1.1

# Delete all rate limit keys
redis-cli --scan --pattern 'rl:*' | xargs redis-cli DEL

Test Rate Limiting

bash
# Hit endpoint multiple times
for i in {1..15}; do
  curl -i http://localhost:3000/api/data
  echo "Request $i"
done

# Check headers
curl -i http://localhost:3000/api/data | grep -i ratelimit

Debug Checklist

  • Redis is running and accessible
  • Plugin registered in module
  • Decorator applied to endpoint
  • Guard is active (auto with decorator)
  • Key extraction working correctly
  • Headers enabled in config
  • CORS configured (if cross-origin)
  • Redis keys exist (check with redis-cli)
  • TTL set on keys
  • Logs show rate limit checks

Common Misconfigurations

typescript
// Points too high - no effective limit
@RateLimit({ points: 1000000, duration: 1 })

// Duration too long - not useful
@RateLimit({ points: 10, duration: 86400 })

// Wrong key type
@RateLimit({ key: 'user' })  // But no authentication!

// Multiple same decorators
@RateLimit({ points: 100 })
@RateLimit({ points: 100 })  // Duplicate, confusing

// Correct configuration
@RateLimit({
  key: 'user',
  algorithm: 'sliding-window',
  points: 100,
  duration: 60,
})

Getting Help

If you're still stuck:

  1. Check Redis connection: redis-cli ping
  2. Enable debug logging
  3. Inspect Redis keys: redis-cli --scan --pattern 'rl:*'
  4. Check application logs for errors
  5. Verify request headers and user context
  6. Test with simple configuration first
  7. Review Configuration and Key Extraction docs

Next Steps

Released under the MIT License.