Enable CORS in Spring 5 Webflux?

How to enable CORS in a Spring 5 Webflux Project?

I cannot find any proper documentation.


I had success with this custom filter:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;

import reactor.core.publisher.Mono;


@Configuration
public class CorsConfiguration {

  private static final String ALLOWED_HEADERS = "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN";
  private static final String ALLOWED_METHODS = "GET, PUT, POST, DELETE, OPTIONS";
  private static final String ALLOWED_ORIGIN = "*";
  private static final String MAX_AGE = "3600";

  @Bean
  public WebFilter corsFilter() {
    return (ServerWebExchange ctx, WebFilterChain chain) -> {
      ServerHttpRequest request = ctx.getRequest();
      if (CorsUtils.isCorsRequest(request)) {
        ServerHttpResponse response = ctx.getResponse();
        HttpHeaders headers = response.getHeaders();
        headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
        headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
        headers.add("Access-Control-Max-Age", MAX_AGE);
        headers.add("Access-Control-Allow-Headers",ALLOWED_HEADERS);
        if (request.getMethod() == HttpMethod.OPTIONS) {
          response.setStatusCode(HttpStatus.OK);
          return Mono.empty();
        }
      }
      return chain.filter(ctx);
    };
  }

}

and org.springframework.boot:spring-boot-starter-web should not be included as dependency - filter does not work with it.


Here is another solution with the Webflux Configurer.

Side Note: Its Kotlin Code (copied from my project) but you can easily translate that to Java Code.

@Configuration
@EnableWebFlux
class WebConfig: WebFluxConfigurer
{
    override fun addCorsMappings(registry: CorsRegistry)
    {
        registry.addMapping("/**")
            .allowedOrigins("*") // any host or put domain(s) here
            .allowedMethods("GET, POST") // put the http verbs you want allow
            .allowedHeaders("Authorization") // put the http headers you want allow
    }
}

@Configuration
public class WebFluxConfig {

    @Bean
    public WebFluxConfigurer corsConfigurer() {
        return new WebFluxConfigurerComposite() {

            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedOrigins("*")
                        .allowedMethods("*");
            }
        };
    }
}

which corresponds to:

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurerAdapter() {

        @Override
        public void addCorsMappings(CorsRegistry registry) {

for spring mvc.


If someone wants a Kotlin version of Zufar's answer(works like a charm with webflux routing functions) without additionally figuring out how Kotlin's SAM conversions work, here is the code:

@Bean
fun corsFilter(): WebFilter {
    return WebFilter { ctx, chain ->
        val request = ctx.request
        if (CorsUtils.isCorsRequest(request)) {
            val response = ctx.response
            val headers = response.headers
            headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN)
            headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS)
            headers.add("Access-Control-Max-Age", MAX_AGE)
            headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS)
            if (request.method === HttpMethod.OPTIONS) {
                response.statusCode = HttpStatus.OK
                return@WebFilter Mono.empty<Void>()
            }
        }
        chain.filter(ctx)
    }
}

UPDATE As I started testing it I found one problem with this solution. It's Ok if you really want to allow all methods. But imagine you want to only allow POST and OPTIONS, for example. And the browser is trying to send PUT.

Then a preflight response will basically say "hey, I can only serve POST and OPTIONS, but my HTTP status will be OK if you give me a request with Access-Control-Request-Method=PUT". It should be 403 Forbidden however. What's more, most of those headers, like Access-Control-Allow-Methods should only be added to preflight requests, not all CORS requests. Solution:

@Bean
fun corsWebFilter(): CorsWebFilter {
    val corsConfig = CorsConfiguration()
    corsConfig.allowedOrigins = Arrays.asList(ALLOWED_ORIGINS)
    corsConfig.maxAge = MAX_AGE.toLong()
    //Notice it's singular. Can't be comma separated list
    corsConfig.addAllowedMethod(ALLOWED_METHOD)
    corsConfig.addAllowedHeader(ALLOWED_HEADER)

    val source = UrlBasedCorsConfigurationSource()
    source.registerCorsConfiguration(MATCH_ALL_PATH_SEGMENTS, corsConfig)

    return CorsWebFilter(source)
}

where

const val MATCH_ALL_PATH_SEGMENTS = "/**"

While @Dachstein's answer is correct it still may not work if you have Security enabled. You can read the documentation about this here https://docs.spring.io/spring-security/site/docs/current/reference/html5/#cors but the provided code may not be enough because of missing applyPermitDefaultValues() method.

If so, try the code below:

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.applyPermitDefaultValues();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:8081"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }