Mastering Rate Limiting in Spring Cloud Gateway with Redis and Custom Configurations

This guide explains how Spring Cloud Gateway's RequestRateLimiter works, how to configure Redis‑based token‑bucket throttling, customize key resolution, and implement a custom RateLimiter bean, providing detailed code examples and configuration tips for robust backend traffic control.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Rate Limiting in Spring Cloud Gateway with Redis and Custom Configurations

Overview

The RequestRateLimiter gateway filter uses a RateLimiter implementation to decide whether a request may proceed; otherwise it returns HTTP 429 – Too Many Requests . The filter accepts an optional keyResolver and rate‑limiter‑specific parameters to generate a key (e.g., a Redis key).

The KeyResolver is a bean implementing the KeyResolver interface. In configuration you reference it with SpEL, e.g., #{@userKeyResolver}. The interface is defined as:

public interface KeyResolver {
  Mono<String> resolve(ServerWebExchange exchange);
}

If no key is found, the request is denied by default. You can adjust this behavior with

spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key

and empty-key-status-code.

Note: The RequestRateLimiter cannot be configured using shortcut notation; the following example is invalid:

# INVALID SHORTCUT CONFIGURATION
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}

Using Redis for Rate Limiting

Redis support relies on spring-boot-starter-data-redis-reactive and uses the token‑bucket algorithm.

Configuration properties:

redis-rate-limiter.replenishRate   // tokens added per second (bucket fill rate)
redis-rate-limiter.burstCapacity  // maximum tokens the bucket can hold; zero blocks all requests
redis-rate-limiter.requestedTokens // tokens consumed per request (default 1)

Typical settings:

Set replenishRate and burstCapacity to the same value for a stable rate.

Set burstCapacity higher than replenishRate to allow temporary bursts.

The limiter leaves a pause between bursts based on replenishRate; consecutive bursts may cause dropped requests (HTTP 429). For rates below 1 request/second, configure replenishRate to the desired request count, set requestedTokens to the time span in seconds, and set burstCapacity to replenishRate * requestedTokens (e.g., 1 rps, requestedTokens=60, burstCapacity=60 yields 1 request per minute).

Example YAML configuration:

spring:
  cloud:
    gateway:
      default-filters:
        - StripPrefix=1
      routes:
        - id: o001
          uri: lb://order-service
          predicates:
            - Path=/api-a/**, /api-b/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 1
                redis-rate-limiter.burstCapacity: 2
                redis-rate-limiter.requestedTokens: 1
                keyResolver: "#{@userKeyResolver}"
                #statusCode: INTERNAL_SERVER_ERROR

Key resolver bean example:

@Configuration
public class RedisRateConfig {
  @Bean
  public KeyResolver userKeyResolver() {
    // IP‑based limiting
    return exchange -> Mono.just(
      exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
    );
  }
}

This defines a limit of 1 request per second per IP with a burst of 2. The KeyResolver simply extracts the client IP (not recommended for production without additional safeguards).

Custom Rate Limiter

You can also define a custom rate limiter by implementing the RateLimiter interface and referencing it via SpEL, e.g., #{@userRateLimiter}. The following YAML uses a custom limiter together with the previously defined KeyResolver:

spring:
  cloud:
    gateway:
      default-filters:
        - StripPrefix=1
      routes:
        - id: o001
          uri: lb://order-service
          predicates:
            - Path=/api-a/**, /api-b/**
          filters:
            - name: RequestRateLimiter
              args:
                rate-limiter: "#{@userRateLimiter}"
                keyResolver: "#{@userKeyResolver}"

Custom limiter implementation skeleton:

public class UserRateLimiter implements RateLimiter<UserRateLimiter.Config> {
  @Override
  public Map<String, Config> getConfig() { return null; }
  @Override
  public Class<Config> getConfigClass() { return null; }
  @Override
  public Config newConfig() { return null; }
  @Override
  public Mono<Response> isAllowed(String routeId, String id) { return null; }

  public static class Config {
    @Min(1)
    private int replenishRate;
    @Min(0)
    private int burstCapacity = 1;
    @Min(1)
    private int requestedTokens = 1;
    // getters and setters omitted for brevity
    @Override
    public String toString() {
      return new ToStringCreator(this)
        .append("replenishRate", replenishRate)
        .append("burstCapacity", burstCapacity)
        .append("requestedTokens", requestedTokens)
        .toString();
    }
  }
}

End of guide.

rate-limitingspring-cloud-gateway
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.