Mastering CORS in Spring Boot: From Basics to Full Implementation
Learn how Cross-Origin Resource Sharing (CORS) works, when it’s needed, the role of preflight requests, essential HTTP headers, and step-by-step Spring Boot solutions—including @CrossOrigin annotations, global configuration, and custom filters—to securely enable cross-domain calls in your applications.
Environment: SpringBoot 2.7.16
1. Introduction
Cross-Origin Resource Sharing (CORS) is an HTTP‑header based mechanism that allows a server to indicate which origins (domains, protocols, or ports) are permitted to access its resources. Browsers enforce same‑origin policy, but CORS lets servers relax this restriction by sending appropriate response headers. For requests that may have side effects, browsers first send a preflight OPTIONS request to verify that the server permits the actual request.
When is CORS needed?
Cross‑origin XMLHttpRequest or Fetch API requests.
Web fonts loaded via @font-face .
WebGL textures.
Drawing images or video onto a canvas with drawImage() .
CSS graphics generated from images.
The CORS specification adds several HTTP header fields that let a server declare which origins may access which resources. For methods other than GET (e.g., POST , DELETE ) or for certain MIME types, the browser must first send a preflight OPTIONS request. The server’s response can also indicate whether credentials such as cookies should be included.
What is a preflight request?
A preflight request uses the OPTIONS method and includes the headers Access-Control-Request-Method , Access-Control-Request-Headers , and Origin . Browsers send it automatically when needed; developers usually do not need to create it manually.
<code>OPTIONS /resource/foo
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: origin, x-requested-with
Origin: https://foo.bar.org</code>If the server permits the request, it responds with appropriate CORS headers, for example:
<code>HTTP/1.1 200 OK
Content-Length: 0
Connection: keep-alive
Access-Control-Allow-Origin: https://foo.bar.org
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400</code>CORS HTTP Headers
Access-Control-Allow-Origin : Indicates whether the response can be shared with the given origin.
Access-Control-Allow-Credentials : Indicates if credentials (cookies, HTTP authentication) are allowed.
Access-Control-Allow-Headers : Lists which request headers can be used in the actual request.
Access-Control-Allow-Methods : Lists allowed HTTP methods for the resource.
Access-Control-Expose-Headers : Lists which response headers are safe to expose to the client.
Access-Control-Max-Age : Indicates how long the results of a preflight request can be cached.
Access-Control-Request-Headers : Sent in a preflight request to tell the server which headers will be used.
Access-Control-Request-Method : Sent in a preflight request to tell the server which HTTP method will be used.
Origin : Indicates the origin that initiated the request.
Timing-Allow-Origin : Allows a specific origin to access the Resource Timing API data.
2. Practical Example
2.1 @CrossOrigin
The @CrossOrigin annotation enables CORS on controller methods:
<code>@RestController
@RequestMapping("/accounts")
public class AccountController {
@CrossOrigin
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}</code>By default, @CrossOrigin allows all origins, all headers, and all HTTP methods mapped to the controller method. Note that allowCredentials is disabled by default; enabling it requires specifying explicit origins.
Note: Enabling allowCredentials without restricting origins can expose sensitive user data such as cookies or CSRF tokens.
The maxAge attribute defaults to 30 minutes.
You can place @CrossOrigin at the class level to apply to all methods:
<code>@CrossOrigin(origins = "https://www.pack.com", maxAge = 3600)
@RestController
@RequestMapping("/accounts")
public class AccountController {
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
}</code>2.2 Global Configuration
Beyond method‑level annotations, you can configure CORS globally via WebMvcConfigurer :
<code>@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://www.pack.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(true)
.maxAge(3600);
}
}</code>By default, global configuration permits all origins, all headers, and the methods GET, HEAD, and POST.
2.3 CORS Filter
The built‑in CorsFilter can also be used. When combined with Spring Security, remember that Spring Security already provides CORS support.
<code>@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://www.pack.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}</code>Alternatively, you can implement a custom filter:
<code>@WebFilter("/*")
public class WebCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Account, access-token");
chain.doFilter(req, res);
}
}</code>That concludes the article; hope it helps you configure CORS securely in your Spring Boot applications.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.