Skip to main content
This guide walks you through adding rate limiting to your Spring Boot application using Redis. You’ll go from zero to a working rate-limited endpoint in just a few steps.

Prerequisites

Before you begin, make sure you have:
  • Java 17 or higher
  • Spring Boot 3.x application
  • Access to a Redis instance (or Docker to run one locally)

Step-by-step guide

1

Add the dependency

Add the Spring Boot Redis Rate Limiter starter to your project:
<dependency>
  <groupId>io.github.v4run-sharma</groupId>
  <artifactId>spring-boot-starter-redis-ratelimiter</artifactId>
  <version>1.0.1</version>
</dependency>
The starter uses Spring Boot’s auto-configuration, so no manual bean wiring is required.
2

Start Redis

You need a running Redis instance. If you don’t have one, start Redis using Docker:
docker run --name redis-ratelimiter -p 6379:6379 -d redis:7-alpine
This command starts Redis 7 on port 6379 in the background.
3

Configure Redis connection

Add Redis connection properties to your application.yml or application.properties:
spring:
  data:
    redis:
      host: localhost
      port: 6379
If you’re using a cloud Redis service, update the host and port accordingly. You can also configure authentication using spring.data.redis.password.
4

Create a service with @RateLimit

Add the @RateLimit annotation to any service method you want to protect. Here’s a billing service example:
BillingService.java
import io.github.v4runsharma.ratelimiter.annotation.RateLimit;
import java.util.concurrent.TimeUnit;
import org.springframework.stereotype.Service;

@Service
public class BillingService {

  @RateLimit(
      name = "invoice-create",
      scope = "GLOBAL",
      limit = 10,
      duration = 1,
      timeUnit = TimeUnit.MINUTES
  )
  public String createInvoice(String accountId) {
    return "Invoice created for account: " + accountId;
  }
}
This configuration allows 10 requests per minute globally across all users.
The name attribute is used for metrics and logging. The scope helps organize rate limits by category.
5

Create a REST controller

Expose your rate-limited service through a REST endpoint:
BillingController.java
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/billing")
public class BillingController {

  private final BillingService billingService;

  public BillingController(BillingService billingService) {
    this.billingService = billingService;
  }

  @PostMapping("/invoices")
  public String createInvoice(@RequestParam String accountId) {
    return billingService.createInvoice(accountId);
  }
}
6

Start your application

Run your Spring Boot application:
./mvnw spring-boot:run
Or if you’re using Gradle:
./gradlew bootRun
Watch for the auto-configuration log messages confirming the rate limiter is active.
7

Test the rate limit

Make repeated requests to your endpoint to trigger the rate limit. You can use curl in a loop:
for i in {1..15}; do
  curl -X POST "http://localhost:8080/api/billing/invoices?accountId=ACC123"
  echo ""
done
The first 10 requests should succeed with a 200 response. Starting from the 11th request, you’ll see rate limit errors.
8

Observe the HTTP 429 response

When the rate limit is exceeded, you’ll receive an HTTP 429 Too Many Requests response with detailed information:
{
  "type": "about:blank",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "Rate limit exceeded: invoice-create (limit=10, window=PT1M, remaining=50)",
  "timestamp": "2026-03-03T10:30:00Z",
  "key": "GLOBAL:com.example.BillingService#createInvoice",
  "limit": 10,
  "windowSeconds": 60,
  "name": "invoice-create",
  "retryAfterSeconds": 50
}
The response includes helpful headers:
  • Retry-After: Seconds until you can retry
  • RateLimit-Limit: Maximum requests allowed
  • RateLimit-Remaining: Requests remaining (0 when blocked)
  • RateLimit-Reset: Seconds until the window resets
The response follows RFC 7807 Problem Details format, making it easy to parse and handle in client applications.
9

Verify Redis keys

Check that rate limit data is being stored in Redis:
docker exec -it redis-ratelimiter redis-cli
Then in the Redis CLI:
KEYS ratelimiter:*
GET ratelimiter:GLOBAL:com.example.BillingService#createInvoice
You’ll see the current request count and the key’s TTL (time to live).
10

Configure optional settings

Customize the rate limiter behavior by adding properties to your configuration:
application.yml
ratelimiter:
  enabled: true
  redis-key-prefix: ratelimiter
  fail-open: false
  include-http-headers: true
  metrics-enabled: true
Configuration options:
  • enabled: Enable or disable rate limiting globally
  • redis-key-prefix: Prefix for Redis keys (default: ratelimiter)
  • fail-open: If true, allows requests when Redis is unavailable
  • include-http-headers: Include rate limit headers in 429 responses
  • metrics-enabled: Enable Micrometer metrics collection
Set fail-open: true in development to continue working even if Redis is down. Keep it false in production for strict rate limiting.

What happens under the hood

When a request hits a rate-limited method:
  1. The AOP interceptor detects the @RateLimit annotation
  2. The policy provider resolves the limit and window duration
  3. The key resolver generates a Redis key based on scope and method
  4. Redis executes INCR on the key and sets a TTL if needed
  5. If the count exceeds the limit, a RateLimitExceededException is thrown
  6. The exception handler converts it to an HTTP 429 response
  7. Metrics are recorded for monitoring
The implementation uses a fixed-window algorithm with Redis INCR and TTL commands. No Lua scripts are required, making it simple and Redis cluster-compatible.

Next steps

Now that you have basic rate limiting working, you can:
For production deployments, consider combining this method-level rate limiting with API gateway-level throttling for defense in depth.