Why Use Spring Cloud Gateway? A Beginner’s Guide to Building a SpringBoot API Gateway

In a micro‑service architecture, a centralized gateway eliminates the need for front‑ends to call dozens of services by handling authentication, rate‑limiting, logging, CORS, and security, and Spring Cloud Gateway—built on WebFlux and Netty—offers a non‑blocking, high‑throughput alternative to Zuul with detailed configuration and code examples for production use.

Java Tech Workshop
Java Tech Workshop
Java Tech Workshop
Why Use Spring Cloud Gateway? A Beginner’s Guide to Building a SpringBoot API Gateway

Why Use a Gateway?

Without a gateway, front‑ends must integrate with dozens or hundreds of service endpoints, scattering authentication, rate‑limiting, logging, CORS, and risk controls across services, leading to operational explosion and security loss of control.

Unified entry point for all clients (Web/APP/mini‑program)

Common cross‑cutting concerns (auth, token validation, CORS, logging, rate‑limiting, IP black/white list, risk interception) are written once in the gateway and shared by all services.

Fine‑grained traffic governance: load balancing, gray routing, weight distribution, request rewriting, path forwarding.

Security protection: block illegal requests, malicious parameters, unauthorized access.

Gateway Underlying Architecture

Technology stack

Underlying network framework: Netty

Programming model: Spring WebFlux (reactive, asynchronous, non‑blocking)

Container: Reactor

Features: single‑thread model, high concurrency, high throughput, low latency

Difference from Zuul

Zuul 1: Servlet‑based blocking model, one thread per request, low throughput, deprecated.

Spring Cloud Gateway: Non‑blocking asynchronous model, a few threads can handle massive requests, performance 5‑10× higher.

Complete execution chain

Client request → Netty receives request → Gateway handler → Predicate matches route → Pre‑filter → load‑balance selects instance → forward to business service → response returns → Post‑filter → response to client.

Three Core Components of Gateway

Route : the smallest unit, containing ID, URI, match rules, and filter list.

Predicate : Java 8 functional predicate that decides whether the current request matches the route; all predicates must pass for the route to be used.

Filter : divided into pre‑filter and post‑filter, used to modify request/response, intercept, log, and enforce rate‑limiting or authentication. Execution order: pre‑filter → business call → post‑filter.

Building a Production‑Grade Gateway from Scratch

Core Dependencies

Do NOT import spring-boot-starter-web to avoid startup conflicts.

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.7.15</version>
</parent>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>2021.0.5</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-dependencies</artifactId>
      <version>2021.0.5.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <!-- Gateway core -->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
  </dependency>
  <!-- Nacos service discovery -->
  <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  </dependency>
  <!-- Redis for rate limiting -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
  </dependency>
  <!-- Sentinel for circuit breaking -->
  <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  </dependency>
</dependencies>

Bootstrap Class

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class GatewayServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayServerApplication.class, args);
    }
}

YAML Configuration (routes, timeout, CORS, retry, rate‑limit)

server:
  port: 8888

spring:
  application:
    name: gateway-server

redis:
  host: 127.0.0.1
  port: 6379

cloud:
  nacos:
    discovery:
      server-addr: 127.0.0.1:8848

gateway:
  httpclient:
    connect-timeout: 2000
    response-timeout: 5000
  discovery:
    locator:
      enabled: false
      lower-case-service-id: true
  default-filters:
    - name: Retry
      args:
        retries: 1
        statuses: 500,502,503
  routes:
    - id: user-service-route
      uri: lb://user-service
      predicates:
        - Path=/user/**
        - Method=GET,POST
      filters:
        - StripPrefix=1
        - AddRequestHeader=source,gateway
        - name: RequestRateLimiter
          args:
            redis-rate-limiter.replenishRate: 10
            redis-rate-limiter.burstCapacity: 20
            key-resolver: "#{@ipKeyResolver}"
    - id: order-service-route
      uri: lb://order-service
      predicates:
        - Path=/order/**
      filters:
        - StripPrefix=1
  globalcors:
    cors-configurations:
      '[/**]':
        allowedOrigins: "*"
        allowedMethods:
          - GET
          - POST
          - PUT
          - DELETE
        allowedHeaders: "*"
        allowCredentials: false

Nine Common Predicate Examples

predicates:
  - Path=/user/**
  - Method=GET,POST
  - Header=token,\w+
  - Query=username
  - RemoteAddr=127.0.0.1/24
  - Host=**.test.com
  - After=2024-01-01T00:00:00+08:00[Asia/Shanghai]
  - Before=2099-01-01T00:00:00+08:00[Asia/Shanghai]
  - Between=2024-01-01T00:00:00+08:00[Asia/Shanghai],2099-01-01T00:00:00+08:00[Asia/Shanghai]

Common Built‑in Filters

filters:
  - StripPrefix=1            # remove first path segment
  - PrefixPath=/api        # add a common prefix
  - AddRequestHeader=gateway,yes
  - AddResponseHeader=server,gateway
  - SetStatus=201
  - RedirectTo=302,https://www.baidu.com

IP‑Based Rate‑Limit Key Resolver

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

@Configuration
public class RateLimitConfig {
    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostString());
    }
}

Global Logging Filter

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Slf4j
@Configuration
public class LogFilterConfig {
    @Bean
    @Order(-100)
    public GlobalFilter logGlobalFilter() {
        return (exchange, chain) -> {
            String path = exchange.getRequest().getPath().value();
            String method = exchange.getRequest().getMethodValue();
            String ip = exchange.getRequest().getRemoteAddress().getHostString();
            log.info("[Gateway request] IP:{}, Method:{}, Path:{}", ip, method, path);
            return chain.filter(exchange)
                .doOnSuccess(v -> {
                    int code = exchange.getResponse().getStatusCode().value();
                    log.info("[Gateway response] Path:{}, Status:{}", path, code);
                });
        };
    }
}

Global Token Authentication Filter

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.core.annotation.Order;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Configuration
public class AuthFilterConfig {
    private static final List<String> WHITE_LIST = Arrays.asList("/user/login", "/user/register");

    @Bean
    @Order(0)
    public GlobalFilter authFilter() {
        return (exchange, chain) -> {
            String path = exchange.getRequest().getPath().value();
            if (WHITE_LIST.contains(path)) {
                return chain.filter(exchange);
            }
            String token = exchange.getRequest().getHeaders().getFirst("token");
            if (token == null || token.isEmpty()) {
                log.error("Request missing token, blocked path: {}", path);
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            return chain.filter(exchange);
        };
    }
}

Global Exception Handler (500)

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GatewayExceptionHandler {
    @ExceptionHandler(Exception.class)
    public Mono<Void> exceptionHandler(ServerHttpRequest request, ServerHttpResponse response, Exception e) {
        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        Map<String, Object> result = new HashMap<>();
        result.put("code", 500);
        result.put("msg", "Gateway service exception, please retry later");
        result.put("path", request.getPath().value());
        return response.writeWith(Mono.just(response.bufferFactory().wrap(result.toString().getBytes())));
    }
}

IP Black‑list Filter

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Configuration
public class BlackIpFilterConfig {
    private static final List<String> BLACK_IP = Arrays.asList("192.168.1.88", "127.0.0.99");

    @Bean
    @Order(-50)
    public GlobalFilter blackIpFilter() {
        return (exchange, chain) -> {
            String ip = exchange.getRequest().getRemoteAddress().getHostString();
            if (BLACK_IP.contains(ip)) {
                log.warn("Blocked black‑list IP: {}", ip);
                exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
                return exchange.getResponse().setComplete();
            }
            return chain.filter(exchange);
        };
    }
}

Sentinel Circuit‑Breaker Configuration

spring:
  cloud:
    gateway:
      routes:
        - id: order-service-route
          uri: lb://order-service
          predicates:
            - Path=/order/**
          filters:
            - StripPrefix=1
            - name: CircuitBreaker
              args:
                name: orderCircuitBreaker
                fallbackUri: forward:/gateway/fallback

Fallback Controller for Circuit‑Breaker

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;

@RestController
public class GatewayFallbackController {
    @GetMapping("/gateway/fallback")
    public Map<String, Object> fallback() {
        Map<String, Object> map = new HashMap<>();
        map.put("code", 503);
        map.put("msg", "Service busy, circuit‑breaker triggered, please retry later");
        return map;
    }
}

Dynamic Routing

Static YAML routes require a gateway restart to take effect, whereas dynamic routing can be refreshed at runtime via a configuration center or database, supporting gray releases and emergency shutdowns. The core mechanism uses RouteDefinitionWriter to add, delete, or update routes without restarting.

Common Troubleshooting Tips

404 error: usually caused by missing StripPrefix, incorrect path matching, or service not registered.

503 error: service down, no instance in registry, or load‑balancer failure.

Startup conflict: importing spring-boot-starter-web causes dependency clash.

Rate limiting not effective: missing KeyResolver or Redis environment.

CORS errors: global CORS configuration missing or lower priority than other filters.

Key Takeaways

Spring Cloud Gateway is the official next‑generation gateway, built on WebFlux + Netty, offering far higher throughput than Zuul.

The three core components are Route, Predicate, and Filter.

Execution flow: request → predicate match → pre‑filter → load‑balance → post‑filter → response.

Gateway provides unified routing, load balancing, authentication, rate limiting, logging, CORS handling, and security protection.

Production deployments require clustering, dynamic governance, and comprehensive traffic protection to ensure high availability of the micro‑service entry point.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

microservicesapi-gatewaynettyspring-bootsecurityRate Limitingspring-cloud-gatewayspring-webflux
Java Tech Workshop
Written by

Java Tech Workshop

Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.

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.