SoFunction
Updated on 2025-04-14

A comprehensive guide to handling cross-domain requests in SpringBoot

1. Basic CORS concept

1.1 What is cross-domain request

Cross-Origin Resource Sharing (CORS) is a security mechanism that allows web applications to request resources on one domain on another domain. For security reasons, browsers will block AJAX requests between different sources, which is a limitation of the **Same-Origin Policy (Same-Origin Policy).

1.2 Definition of homologous policy

The conditions for two URLs to be considered homologous:

  • The protocol is the same (http/https)
  • The domain name is the same
  • Same port

For example:

  • /app1 and /app2 → Same source
  • and → different sources (different protocols)
  • and → different sources (domain names are different)
  • And :8080 → Different sources (different ports)

2. 5 ways Spring Boot handles CORS

2.1 Global Configuration (Recommended)

Method 1: Use WebMvcConfigurer interface

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        ("/**")  // All interfaces                .allowedOrigins("*")  // All sources are allowed                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")  // Allow method                .allowedHeaders("*")  // All headers are allowed                .allowCredentials(true)  // Allow credentials                .maxAge(3600);  // Preflight request cache time    }
}

Method 2: Use Filter method (suitable for Servlet applications)

@Bean
public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    
    (true);
    ("*");
    ("*");
    ("*");
    ("/**", config);
    
    return new CorsFilter(source);
}

2.2 Controller method level configuration

Method 3: Use @CrossOrigin annotation

@RestController
@RequestMapping("/api")
public class UserController {
    
    // Single method configuration    @CrossOrigin(origins = "http://localhost:3000")
    @GetMapping("/users")
    public List<User> getUsers() {
        // ...
    }
    
    // The entire controller configuration    @CrossOrigin(origins = "*", maxAge = 3600)
    @RestController
    @RequestMapping("/products")
    public class ProductController {
        // ...
    }
}

2.3 Properties file configuration (Spring Boot 2.4+)

Method 4: Configure

spring:
  mvc:
    cors:
      allowed-origins: "http://localhost:3000, "
      allowed-methods: "GET, POST, PUT, DELETE"
      allowed-headers: "*"
      exposed-headers: "Authorization, Content-Disposition"
      allow-credentials: true
      max-age: 1800

Equivalent:

-origins=http://localhost:3000, 
-methods=GET, POST, PUT, DELETE
-headers=*
-headers=Authorization, Content-Disposition
-credentials=true
-age=1800

2.4 Manual setting of response headers

Method 5: Add response headers manually (flexible but cumbersome)

@RestController
public class ApiController {
    
    @GetMapping("/manual")
    public ResponseEntity<String> manualCors() {
        return ()
                .header("Access-Control-Allow-Origin", "*")
                .header("Access-Control-Allow-Methods", "GET")
                .body("Manual CORS configured");
    }
}

3. In-depth analysis of CORS processing

3.1 Preflight Request

For "non-simple request", the browser will first send an OPTIONS preflight request:

Simple request conditions:

1. Use GET, HEAD or POST methods

2. Only include the following headers:

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (only application/x-www-form-urlencoded, multipart/form-data, text/plain)

Non-simple request example:

fetch('/data', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'X-Custom-Header': 'value'
  },
  body: ({key: 'value'})
});

Spring Boot will automatically process OPTIONS requests without the need for additional encoding by developers.

3.2 Core response header description

Response header illustrate
Access-Control-Allow-Origin The source allowed to access,*Indicates any source
Access-Control-Allow-Methods Allowed HTTP methods
Access-Control-Allow-Headers Allowed request header
Access-Control-Expose-Headers Response header that allows browser access
Access-Control-Allow-Credentials Whether to allow sending cookies and HTTP authentication information
Access-Control-Max-Age Cache time of pre-check request result (seconds)

3.3 Frequently Asked Questions

Question 1: AllowCredentials(true) conflicts with allowedOrigins("*")

mistake:

When allowCredentials is true, allowedOrigins cannot contain the special value "*"

Solution:

// Replace with specific domain name.allowedOrigins("http://localhost:3000", "")

Question 2: The front-end still reports CORS error

Check steps:

  • Make sure the backend is configured correctly
  • Check browser console error details
  • Use Postman and other tools to verify that the interface is working properly
  • Check if multiple CORS configurations are overwhelmed

Question 3: Custom filter interferes with CORS

Solution:

Ensure CorsFilter priority in filter chain:

@Bean
public FilterRegistrationBean&lt;CorsFilter&gt; corsFilterRegistration() {
    FilterRegistrationBean&lt;CorsFilter&gt; registration = new FilterRegistrationBean&lt;&gt;();
    (corsFilter());
    (Ordered.HIGHEST_PRECEDENCE);  // Highest priority    return registration;
}

4. Safety best practices

4.1 Production environment configuration suggestions

@Configuration
public class ProdCorsConfig implements WebMvcConfigurer {
    
    @Value("${-origins}")
    private String[] allowedOrigins;
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        ("/api/**")
                .allowedOrigins(allowedOrigins)
                .allowedMethods("GET", "POST")
                .allowedHeaders("Content-Type", "Authorization")
                .exposeHeaders("X-Custom-Header")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

4.2 Combined with Spring Security

When using Spring Security, you need to make sure that CORS is configured before the security filter:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ().and()  // Enable CORS support for Spring Security            .csrf().disable()
            .authorizeRequests()
            // Other configurations...    }
    
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        ((""));
        (("GET", "POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        ("/**", configuration);
        return source;
    }
}

4.3 Monitoring and logging

Add CORS request log:

@Bean
public FilterRegistrationBean&lt;CorsFilter&gt; corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    // ...Configuration    
    FilterRegistrationBean&lt;CorsFilter&gt; registration = new FilterRegistrationBean&lt;&gt;();
    (new CorsFilter(source) {
        @Override
        protected void doFilterInternal(HttpServletRequest request, 
            HttpServletResponse response, FilterChain filterChain) 
            throws ServletException, IOException {
            
            ("CORSask: {} {}", (), ());
            (request, response, filterChain);
        }
    });
    return registration;
}

V. Testing and Verification

5.1 Test class example

@SpringBootTest
@AutoConfigureMockMvc
public class CorsTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    public void testCorsHeaders() throws Exception {
        (options("/api/users")
                .header("Access-Control-Request-Method", "GET")
                .header("Origin", "http://localhost:3000"))
                .andExpect(header().exists("Access-Control-Allow-Origin"))
                .andExpect(header().string("Access-Control-Allow-Methods", "GET"));
    }
    
    @Test
    public void testActualRequest() throws Exception {
        (get("/api/users")
                .header("Origin", "http://localhost:3000"))
                .andExpect(status().isOk())
                .andExpect(header().string("Access-Control-Allow-Origin", "http://localhost:3000"));
    }
}

5.2 Testing with CURL

Check OPTIONS preflight request:

curl -X OPTIONS http://localhost:8080/api/users \
-H "Origin: " \
-H "Access-Control-Request-Method: GET" \
-I

Check the actual request:

curl -X GET http://localhost:8080/api/users \
-H "Origin: " \
-I

6. Summary and Recommended Plan

6.1 Configuration method comparison

Way Applicable scenarios advantage shortcoming
Global WebMvcConfigurer Most applications Centralized management, support fine-grained configuration Need code changes
Filter method Need the highest priority The earliest processing is to avoid being interfered by other filters A little more complex configuration
@CrossOrigin annotation Specific interfaces require special rules Precise control Spread all over the place, with high maintenance costs
Properties file configuration Simple requirements, configuration driver No code changes required Low flexibility
Manually set the response header Need to dynamically determine the CORS header Maximum flexibility Highly invasive code

6.2 Recommended plan

New Project:

  • Global configuration using WebMvcConfigurer
  • Dynamically configure the allowed sources in conjunction with attribute files
  • Use @CrossOrigin to override global settings for special interfaces

Already project migration:

  • Add global configuration first
  • Gradually remove scattered annotation configurations
  • Finally, unify 1-2 management methods

Spring Cloud Microservices:

  • Unified processing of CORS in API Gateway
  • Each microservice disables CORS or only allows gateway sources
  • Combined with OAuth2 and other security mechanisms

6.3 Ultimate Suggestions

Do not use * as an allowable source in production - explicitly list trusted domain names

Limit allowed methods and headers - Configure according to the principle of least permissions

Set maxAge reasonably - balance safety and performance (1 hour recommended)

Collaborate with the front-end team - Ensure that both parties are consistent in their understanding of CORS requirements

Monitor CORS errors - promptly detect configuration problems or malicious requests

By rationally configuring CORS, it is possible to ensure security while providing necessary cross-domain support for modern front- and back-end separation architectures. Spring Boot provides a variety of flexible ways, and developers should choose the most suitable solution according to the actual needs of the project.

This is the article about this comprehensive guide to handling cross-domain requests for CORS in SpringBoot. For more relevant SpringBoot cross-domain requests for CORS, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!