Spring Cloud Gateway
Spring Cloud Gateway
In the previous lesson you learned the theoretical case for an API Gateway. This lesson gets practical: you will build a working gateway with Spring Cloud Gateway (the reactive, non-blocking implementation built on Spring WebFlux and Project Reactor). By the end you will understand how to declare routes, compose predicates, apply filters, and reason about the security and operational implications of every decision.
Adding the Dependency
Spring Cloud Gateway is a separate starter. Create a new Spring Boot 3 project (or module) and add it to pom.xml:
spring-boot-starter-web alongside the gateway starter. Spring Cloud Gateway is built on WebFlux (reactive, non-blocking). Mixing it with the servlet-based spring-boot-starter-web on the same classpath causes a startup conflict. Use only the gateway starter; if you need security add spring-boot-starter-security and the reactive variant of Security will be configured automatically.
The Route: The Fundamental Building Block
A route is the core concept in Spring Cloud Gateway. Each route has three parts:
- ID — a unique string used in logs and metrics.
- URI — the destination to forward matched requests to.
- Predicates — conditions that must ALL be true for the route to match a request.
- Filters — transformations applied to the request before forwarding or to the response before returning it.
Routes can be declared in application.yml (most common), in a RouteLocator Java bean, or dynamically via a discovery client. Start with YAML:
What this does: any request whose path starts with /api/orders/ is forwarded to http://localhost:8081. The StripPrefix=1 filter removes the first path segment (/api) before forwarding, so GET /api/orders/42 becomes GET /orders/42 on the downstream service.
Predicates in Depth
A predicate is a boolean test against the incoming ServerWebExchange. Spring Cloud Gateway ships with a rich set of built-in predicates. Multiple predicates on one route are ANDed together.
Path predicate — matches on URL path with Ant-style wildcards:
Method predicate — restricts by HTTP verb:
Header predicate — matches when a header exists and its value satisfies a regular expression:
Query predicate — matches when a query parameter is present (and optionally matches a regex):
After / Before / Between predicates — time-windowed routing, useful for scheduled maintenance or feature flags:
Weight predicate — used for canary deployments; routes a percentage of traffic to a new version:
Path=/** catch-all route should always be last.
Gateway Filters in Depth
Filters operate on the exchange. GatewayFilter applies to a single route; GlobalFilter applies to every route. Built-in filters cover the most common gateway concerns.
AddRequestHeader — inject a header before forwarding:
Downstream services can check this header to ensure requests came through the gateway, not directly.
AddResponseHeader — inject a header on the way back:
RewritePath — full regex-based path rewriting, more powerful than StripPrefix:
RequestRateLimiter — built-in token-bucket rate limiting backed by Redis. Add the Redis reactive starter, then configure:
The key resolver determines how to identify a caller. The simplest resolver uses the remote IP address:
In production, resolve on a JWT subject or API key instead of IP so that NAT addresses do not unfairly share a bucket.
CircuitBreaker filter — integrates with Resilience4j to open the circuit when a downstream service is failing:
Writing a Custom GatewayFilter
When built-in filters are insufficient you can write your own. Implement GatewayFilterFactory and register it as a Spring bean:
Use the factory by name in YAML (the class name without GatewayFilterFactory suffix):
GatewayFilterFactory beans by class name. The YAML key must exactly match the prefix before GatewayFilterFactory (case-sensitive). Getting this wrong results in a FilterDefinitionNotFoundException at startup.
Java DSL Routes
YAML is convenient for simple routes, but the Java DSL gives you full IDE support, type safety, and programmatic logic (e.g. reading routes from a database):
The lb://order-service URI scheme tells the gateway to resolve the service name through the Eureka registry and load-balance across its instances using Spring Cloud LoadBalancer — the same mechanism covered in Lesson 3.
Security Implications
- Never trust headers injected by callers. If downstream services rely on
X-User-IdorX-Rolesheaders forwarded by the gateway, strip those headers from the incoming request first, then add your own after authentication. Otherwise any client can forge them. - Gateway is not a firewall. Ensure downstream services are not directly reachable from outside your private network. The gateway is the single ingress point by policy and by network topology — not just by convention.
- Log correlation IDs. Add a
GlobalFilterthat generates a UUID per request, attaches it as a header (X-Correlation-Id), and passes it downstream. This makes distributed tracing across multiple services possible.
Summary
Spring Cloud Gateway is configured around routes, each of which matches incoming requests via composable predicates and transforms them via filters. Predicates cover path, method, header, query, time, and weight-based traffic splitting. Built-in filters handle path rewriting, header injection, rate limiting, and circuit breaking; custom GatewayFilterFactory beans let you add any cross-cutting logic you need. Use the lb:// URI scheme to integrate seamlessly with Eureka-registered services. In the next lesson you will see how authentication, CORS, logging, and rate limiting are layered on top of the gateway as cross-cutting concerns.