Over a million developers have joined DZone.

Dynamic Routing Through Zuul With a REST API and Spring Boot Without Spring Config — Sub-Domain to Subpath Router

DZone 's Guide to

Dynamic Routing Through Zuul With a REST API and Spring Boot Without Spring Config — Sub-Domain to Subpath Router

Learn about using Zuul with a REST API to route traffic from one service to another with no downtime.

· Microservices Zone ·
Free Resource

Problem Statement

Say there was a requirement to forward all the traffic coming to *.adomain.com to adomain.com/*. For Example: a.adomain.com/**/* should be forwarded to adomain.com/a/**/*. Additionally, the proxy gateway should be up all the time and new subdomains need to be registered in the gateway without any downtime. This problem was a very specific one and needed some research to achieve the same. 

Research Around Proxies

We tried a couple of things, including node http-proxy, the Java throo library (internally using Zuul), and Zuul itself. All the examples were based on prefetched configuration from a properties file for the proxy creation, except advance Zuul examples, which talk about refreshable configuration using Spring refreshable cloud configuration and RabbitMQ. There was a need for a runtime proxy creator service, preferably exposing a POST API taking some parameters and registering a new proxy at runtime. Even a delete API was required to get rid of proxies no longer being used. Being a Java developer, the natural choice was Zuul.


Knowledge of Java, APIs, proxy, Apache, the gateway concept, and Spring Boot is needed to fully grasp the idea presented here.


While going through Zuul provided by Netflix, now added in Spring Cloud distribution, there is a nice example of how to do it by using Eureka, Zuul Server, Spring Cloud Server, RabbitMQ, and Git. Although the whole example is great and provides great flexibility, it involves many frameworks and lots of setup to be done. I could not use it as I was looking to create a very simple solution to solve our problem, considering the tradeoff of not being HA.

The architecture is as follows:

Image title


There are a couple of things needed to make the application work:


To take the parameter in POST for creating the route.

Zuul-Related Changes

  • Pom changes: It contains actuator, zuul, and spring-cloud related dependencies.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <relativePath />
        <!-- lookup parent from repository -->
        <!-- Dependencies -->
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />

Spring Boot Application class:

package com.mettl.gatewayservice.application;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.ComponentScan;

@EnableAutoConfiguration(exclude = { RabbitAutoConfiguration.class })
public class SpringBootWebApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootWebApplication.class, args);


  port: ${appPort:80}
info.app.version: @project.version@
# Actuator endpoint path (/admin/info, /admin/health, ...)
server.servlet-path: /
management.context-path: /admin
# ribbon.eureka.enabled: false
  ignoredPatterns: /**/admin/**, /proxyurl
      path: /**
      url: http://localhost:8000/
# stripPrefix set to true if context path is set to /
      stripPrefix: true

There are two parts:

1. Registration of Proxy Routes

This part is done by creating a service class by auto-wiring two dependencies, like this:  

public class ZuulDynamicRoutingService {         

private static final String HTTP = "http://";         
private final ZuulProperties zuulProperties;         
private final ZuulHandlerMapping zuulHandlerMapping;


Adding the new route is achieved by the following code.

Creation of uuid can be done by following any standard way. The only thing is that it should be a unique key, as it is going to be used in the map used by zuulProperties.getRoutes().

String uuid = GenerateUID.getUID();
if (StringUtils.isEmpty(dynamicRouteRequest.getSubpath())) {

String url = "http://" + dynamicRouteRequest.getHost() + ":" + dynamicRouteRequest.getPort() + dynamicRouteRequest.getSubpath();
zuulProperties.getRoutes().put(uuid, new ZuulRoute(uuid, "/" + uuid + "/**", null, url, true, false, new HashSet<>()));

This service is injected in a controller to receive the post request and forward the details to the service class to get the proxy created.

It can be checked with the URL /admin/routes.

2. Forwarding HTTP Requests to the Destination With Subdomain to Subpath Conversion

This requires a PreFilter class extending ZuulFilter:

public class PreFilter extends ZuulFilter {

  private static Logger log = LoggerFactory.getLogger(PreFilter.class);

  private UrlPathHelper urlPathHelper = new UrlPathHelper();

  public String filterType() {
      return "pre";

  public int filterOrder() {
      return 1;

  public boolean shouldFilter() {

      RequestContext ctx = RequestContext.getCurrentContext();
      String requestURL = ctx.getRequest().getRequestURL().toString();
      //Here we only require to filter those URLs which contains "proxyurl" and "/admin/".
      return !(requestURL.contains("proxyurl") || requestURL.contains("/admin/"));

  //The actual part where the subdomain to subpath conversion happens is as follows:

  public Object run() {
      RequestContext ctx = RequestContext.getCurrentContext();
      String remoteHost = ctx.getRequest().getRemoteHost();
      String requestURL = ctx.getRequest().getRequestURL().toString();
      if (!requestURL.contains("proxyurl")) {
          log.info("remoteHost {} requestURL {}", new Object[]{remoteHost, requestURL});
          String originatingRequestUri = this.urlPathHelper.getOriginatingRequestUri(ctx.getRequest());
          final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());
          log.info("URI {} original URI {}", new Object[]{requestURI, originatingRequestUri});
          String protocol = requestURL.substring(0, requestURL.indexOf("//") + 2);
          String urlWithoutProtocol = requestURL.substring(requestURL.indexOf("//") + 2);
          String[] split = urlWithoutProtocol.substring(0, urlWithoutProtocol.indexOf("/")).split("\\.");
          String subPath = split[0];
          final String newURL = protocol + "." + split[1] + "." + split[2];
          //Here the main thing is to create a HttpServletRequestWrapper and override the request coming from the actual request
          HttpServletRequestWrapper httpServletRequestWrapper = new HttpServletRequestWrapper(ctx.getRequest()) {
              public String getRequestURI() {
                  if (requestURI != null && !requestURI.equals("/")) {
                      if (!StringUtils.isEmpty(subPath)) {
                          return "/" + subPath + requestURI;
                      } else {
                          return requestURI;
                  if (!StringUtils.isEmpty(subPath)) {
                      return "/" + subPath;
                  } else {
                      return "/";
              public StringBuffer getRequestURL() {
                return new StringBuffer(newURL);
          HttpServletRequest request = ctx.getRequest();
          log.info("PreFilter: " + String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
       return null;

Now you can run the application by adding the missing part with mvn springboot:run or java -jar gateway-service.jar.

Now, use Postman to submit the request, like below.

Image title

To Be Done on Your Own

  • The Apache part is missing in this session. Add a configuration to forward all requests coming to *.adomain.com to the IP hosting the Zuul server with the port configured when starting the Zuul server.

  • Applications where the requests need to be up when creating proxy routes to actually see what is happening.


We learned to

  1. Create a proxy route at runtime by exposing an API 

  2. Change PreFilter (a subclass of ZuulFilter) to pre-process the HTTP request

  3. Add Apache configuration to forward requests to Zuul Gateway

zuul ,rest api ,proxy services ,spring boot ,routing ,tutorial ,microservices

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}