After configuring the CORS settings within my Spring Boot application to only permit requests coming from my website, I encountered an unexpected behavior when deploying the application on App Engine. Despite successful local testing where the configuration worked as intended, after deployment, the API remains accessible from any origin.
Below is my spring security configuration class :
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll()) // Allow all requests
.httpBasic(Customizer.withDefaults())
.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(corsConfigurationSource()))
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://mywebsite.com","https://mywebsite.com"));
configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Origin", "Origin"));
configuration.setAllowedMethods(Arrays.asList("GET","POST","DELETE","PUT"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
And this is my controller:
@RestController
@RequestMapping("/cars")
public class CarController {
private final CarService carService;
public CarController(CarService carService) {
this.carService = carService;
}
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<Car> saveCar(@RequestBody Car car) {
return carService.saveCar(car);
}
@DeleteMapping("/{carId}")
public Mono<Void> deleteCar(@PathVariable String carId) {
return carService.deleteCar(carId);
}
@GetMapping
public Flux<Car> findAllCars() {
return carService.findALlCars();
}
@GetMapping("/city/{city}")
public Flux<Car> findCarsByCity(@PathVariable String city) {
return carService.findCarsByCity(city);
}
}
The app.yml file used by App Engine to deploy the API:
runtime: java17
instance_class: F1
I've found that I could configure CORS in my app.yml file, but I preferred to keep this concern for the spring security to handle it.
Additional logs:


You might need to adjust
configuration.setAllowedHeaders(...). The header"Access-Control-Allow-Origin"is a response header, not a request header. It should not be in the list of allowed headers. Typically, you would allow headers like"Content-Type","Authorization","Cache-Control", etc.Make sure that your
SecurityConfigurationclass is being picked up by Spring Boot. Sometimes, if the configuration is not in the correct package or is missing certain annotations, it will not be used. YourSecurityConfigurationclass should be in a package that is either the same as or a sub-package of the main application class's package. The main application class is the one annotated with@SpringBootApplication.Add logging to the
SecurityConfigurationclass to check whether it is being instantiated. You can add a log statement in a@PostConstructmethod (the constructor might not be the best place to put a log message to test instantiation because Spring manages the creation of beans in a way that may not call the constructor as you might expect):If you have the Spring Boot Actuator dependency in your project, you can use the
/beansendpoint to get a list of all the beans that have been instantiated in the Spring context, including your configuration:Also, an app Engine might be overriding your CORS settings or providing its own defaults. It is possible that other middleware in your application is overriding the headers before they are sent back to the client. Check the logs in Google Cloud Console for your App Engine application to see if there are any messages that indicate manipulation of response headers or errors related to CORS.
As a test, you could temporarily disable your Spring Security configuration and deploy a simple controller that sets CORS headers manually in the response to see if App Engine is the one overriding them.
As noted by dur, the
Originheader is missing from your request.Use tools like
curlto manually send requests with anOriginheader to your App Engine deployment, and to see if the response indicates that CORS is being applied: