Spring Boot
CORS
Cross-Origin Resource Sharing
Web Development
Java Frameworks

CORS issue with Spring Boot

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A CORS problem in Spring Boot usually means the browser sent a cross-origin request and the server did not answer with the headers needed to allow it. The tricky part is that the actual fix depends on where the request is being blocked: controller mapping, global MVC configuration, preflight handling, or Spring Security.

What the Browser Is Expecting

Browsers enforce the same-origin policy. If a frontend from one origin calls an API at another origin, the server must respond with headers such as Access-Control-Allow-Origin.

For non-simple requests, the browser first sends an OPTIONS preflight request. If that preflight fails, your actual GET, POST, or PUT request never leaves the browser.

That is why a backend can appear to work fine in Postman but still fail from the browser.

Controller-Level Configuration

For a small application or a single endpoint, @CrossOrigin is often enough:

java
1import org.springframework.web.bind.annotation.CrossOrigin;
2import org.springframework.web.bind.annotation.GetMapping;
3import org.springframework.web.bind.annotation.RestController;
4
5@RestController
6@CrossOrigin(origins = "http://localhost:3000")
7public class GreetingController {
8
9    @GetMapping("/api/greeting")
10    public String greeting() {
11        return "hello";
12    }
13}

This is convenient when only a few controllers should be callable from the frontend.

Global MVC Configuration

If many endpoints need the same CORS policy, configure it globally:

java
1import org.springframework.context.annotation.Configuration;
2import org.springframework.web.servlet.config.annotation.CorsRegistry;
3import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
4
5@Configuration
6public class WebConfig implements WebMvcConfigurer {
7    @Override
8    public void addCorsMappings(CorsRegistry registry) {
9        registry.addMapping("/api/**")
10                .allowedOrigins("http://localhost:3000")
11                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
12                .allowedHeaders("*")
13                .allowCredentials(true);
14    }
15}

This is cleaner when the policy is shared across your API.

Spring Security Changes the Picture

A very common mistake is adding MVC CORS config but forgetting that Spring Security also has to allow the request. If Security handles the request first and rejects the preflight, your MVC CORS config never helps.

A modern SecurityFilterChain setup should explicitly enable CORS:

java
1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.security.config.Customizer;
4import org.springframework.security.config.annotation.web.builders.HttpSecurity;
5import org.springframework.security.web.SecurityFilterChain;
6
7@Configuration
8public class SecurityConfig {
9
10    @Bean
11    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
12        http
13            .cors(Customizer.withDefaults())
14            .csrf(csrf -> csrf.disable())
15            .authorizeHttpRequests(auth -> auth
16                .requestMatchers("/api/**").permitAll()
17                .anyRequest().authenticated()
18            );
19
20        return http.build();
21    }
22}

Without .cors(...), developers often see confusing preflight failures and assume the MVC configuration is broken.

Credentials and Wildcards

Another frequent trap is mixing credentials with a wildcard origin. If the browser sends cookies or authorization headers, you cannot use * together with allowCredentials(true) in the way many people first try.

Instead, specify the exact allowed origin or a controlled pattern.

That matters especially for session-based applications where the frontend and backend run on different local ports during development.

How to Debug It

When debugging a CORS problem, check these in the browser network tab:

  • the preflight OPTIONS request
  • the response status code
  • 'Access-Control-Allow-Origin'
  • 'Access-Control-Allow-Methods'
  • 'Access-Control-Allow-Headers'
  • whether credentials are being sent

Also compare the frontend origin exactly, including scheme and port. http://localhost:3000 and http://localhost:5173 are different origins.

Common Pitfalls

  • Configuring CORS in controllers but forgetting to enable it in Spring Security is one of the most common failures.
  • Using * while also expecting cookies or authenticated cross-origin requests usually does not behave the way developers want.
  • Ignoring the browser preflight request makes debugging much harder because the real request may never be sent.
  • Testing only with Postman can hide the issue because CORS is a browser security rule, not a generic HTTP rule.
  • Allowing every origin in production is easy but often a bad security choice when the API is supposed to be used only by known frontends.

Summary

  • A Spring Boot CORS issue is usually about missing or mismatched response headers for a browser cross-origin request.
  • '@CrossOrigin works well for small, local cases.'
  • Global MVC configuration is better when the policy applies across many endpoints.
  • If Spring Security is present, enable CORS there too or preflight requests may still fail.
  • Always debug with the browser network panel, because Postman does not reproduce browser CORS enforcement.

Course illustration
Course illustration

All Rights Reserved.