I'm developing a spring backend for a react-based single page application where I'm using react-router for client-side routing.

Beside the index.html page the backend serves data on the path /api/**.

In order to serve my index.html from src/main/resources/public/index.html on the root path / of my application I added a resource handler

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/").addResourceLocations("/index.html");
}

What I want to is to serve the index.html page whenever no other route matches, e.g. when I call a path other than /api.

How do I configure such catch-all route in spring?


Since my react app could use the root as forward target this ended up working for me

@Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {

  @Override
  public void addViewControllers(ViewControllerRegistry registry) {
      registry.addViewController("/{spring:\\w+}")
            .setViewName("forward:/");
      registry.addViewController("/**/{spring:\\w+}")
            .setViewName("forward:/");
      registry.addViewController("/{spring:\\w+}/**{spring:?!(\\.js|\\.css)$}")
            .setViewName("forward:/");
  }
}

To be honest I have no idea why it has to be exactly in this specific format to avoid infinite forwarding loop.


I have a Polymer-based PWA hosted inside of my Spring Boot app, along with static web resources like images, and a REST API under "/api/...". I want the client-side app to handle the URL routing for the PWA. Here's what I use:

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    /**
     * Ensure client-side paths redirect to index.html because client handles routing. NOTE: Do NOT use @EnableWebMvc or it will break this.
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // Map "/"
        registry.addViewController("/")
                .setViewName("forward:/index.html");

        // Map "/word", "/word/word", and "/word/word/word" - except for anything starting with "/api/..." or ending with
        // a file extension like ".js" - to index.html. By doing this, the client receives and routes the url. It also
        // allows client-side URLs to be bookmarked.

        // Single directory level - no need to exclude "api"
        registry.addViewController("/{x:[\\w\\-]+}")
                .setViewName("forward:/index.html");
        // Multi-level directory path, need to exclude "api" on the first part of the path
        registry.addViewController("/{x:^(?!api$).*$}/**/{y:[\\w\\-]+}")
                .setViewName("forward:/index.html");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/webapp/");
    }
}

This should work for Angular and React apps as well.


Avoid @EnableWebMvc

By default Spring-Boot serves static content in src/main/resources:

  • /META-INF/resources/
  • /resources/
  • /static/
  • /public/

Take a look at this and this;

Or keep @EnableWebMvc and override addViewControllers

Did you specify @EnableWebMvc ? Take a look a this: Java Spring Boot: How to map my app root (“/”) to index.html?

Either you remove @EnableWebMvc, or you can re-define addViewControllers:

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/").setViewName("forward:/index.html");
}

Or define a Controller to catch /

You may take a look a this spring-boot-reactjs sample project on github:

It does what you want using a Controller:

@Controller
public class HomeController {

    @RequestMapping(value = "/")
    public String index() {
        return "index";
    }

}

Its index.html is under src/main/resources/templates