CORS

About

Cross-Origin Resource Sharing (CORS) is a security feature enforced by web browsers to restrict how resources on a web page can be requested from a different origin (domain, protocol, or port).

By default, browsers block cross-origin HTTP requests made via fetch, XHR (XMLHttpRequest), or AJAX unless explicitly allowed by the server.

For Example,

Blocked by CORS A website running on https://example.com tries to fetch data from https://api.otherdomain.com.

Allowed by CORS (If API Allows) The server at https://api.otherdomain.com responds with a special CORS header (Access-Control-Allow-Origin: https://example.com), telling the browser it’s safe to proceed.

Why is CORS Needed?

Modern web applications often consume APIs hosted on different domains. Without CORS, these requests would be blocked by the browser's Same-Origin Policy (SOP).

CORS bypasses SOP securely by allowing the server to specify which domains can access its resources.

How CORS Works?

Imagine we own a bank website (https://mybank.com) that needs to fetch account details from an API server(https://api.mybank.com).

Problem: By default, web browsers block requests from one domain (mybank.com) to another (api.mybank.com) due to Same-Origin Policy (SOP)—a security rule that prevents unauthorized access to user data.

Solution: The API server (https://api.mybank.com) must explicitly allow requests from https://mybank.com using CORS headers in its response.

CORS Request Flow

1 - The browser sends a request to https://api.mybank.com asking, “Can I access your data?”

GET /accounts
Origin: https://mybank.com

2 - The API server responds with CORS headers, allowing or denying access.

Allowed Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://mybank.com

Blocked Response (No CORS Headers):

HTTP/1.1 403 Forbidden

3 - If allowed, the browser processes the response and displays the data. Otherwise, it blocks the request.

Preflight Requests (Extra Security Check)

If the request uses special HTTP methods (POST, PUT, DELETE) or custom headers, the browser first sends a preflight request (OPTIONS request) to ask:

Browser: "API, are you okay with me sending a DELETE request?"

API Server: "Yes! I allow DELETE requests from https://mybank.com."

Types of CORS Requests

1. Simple Requests

A request is Simple if:

  • It uses GET, POST, or HEAD.

  • It has only allowed headers (e.g., Content-Type: application/x-www-form-urlencoded).

Example (Simple CORS Request)

fetch("https://api.example.com/data", {
    method: "GET",
    headers: {
        "Content-Type": "application/json"
    }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));

Server Response Headers (Success)

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com

2. Preflight Requests (For Non-Simple Requests)

A Preflight Request is an OPTIONS request sent before the actual request to check if the server allows CORS.

  • Happens when using methods like PUT, DELETE, PATCH.

  • Happens when sending custom headers.

  • Ensures security before sending sensitive data.

Example (Preflight Request - Sent by Browser Automatically)

OPTIONS /data HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Authorization

Server Response (Approving CORS Request)

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 3600

Access-Control-Allow-Origin → Specifies allowed domains.

Access-Control-Allow-Methods → Lists allowed HTTP methods.

Access-Control-Allow-Headers → Lists allowed request headers.

Access-Control-Max-Age → Specifies how long the preflight response is cached.

3. Credentialed Requests (Cookies, Authorization Headers, etc.)

If a request includes credentials (cookies, authentication headers), the server must allow it explicitly.

Client Request with Credentials

fetch("https://api.example.com/user", {
    credentials: "include", // Includes cookies
    headers: {
        "Authorization": "Bearer xyz-token"
    }
})

Server Response for Credentialed Requests

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
  • Access-Control-Allow-Credentials: true MUST be set to allow credentials.

  • Access-Control-Allow-Origin: * CANNOT be used with credentials.

How to Enable CORS in Spring Boot

1. Global CORS Configuration (For All Endpoints)

@Configuration
public class CorsConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**") // Apply to all endpoints
                    .allowedOrigins("https://example.com") // Allowed domains
                    .allowedMethods("GET", "POST", "PUT", "DELETE") // Allowed HTTP methods
                    .allowedHeaders("*") // Allow all headers
                    .allowCredentials(true); // Allow credentials (cookies)
            }
        };
    }
}

2. CORS for a Specific Controller

@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "https://example.com", allowedHeaders = "*", methods = {RequestMethod.GET, RequestMethod.POST})
public class SampleController {
    
    @GetMapping("/data")
    public ResponseEntity<String> getData() {
        return ResponseEntity.ok("CORS enabled");
    }
}
  • @CrossOrigin enables CORS only for this controller.

  • The allowedHeaders and methods define which requests are allowed.

3. CORS Configuration for Spring Security

If Spring Security is enabled, we must configure CORS explicitly in SecurityFilterChain.

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .csrf().disable()
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());

        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of("https://example.com"));
        configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
        configuration.setAllowedHeaders(List.of("*"));
        configuration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
  • If Spring Security is enabled, CORS must be configured inside the security filter chain.

  • setAllowedOrigins(List.of("https://example.com")) → Allows requests only from example.com.

  • setAllowCredentials(true) → Allows credentials (cookies, authentication headers).

Common CORS Issues and Fixes

Issue

Cause

Fix

CORS policy error: No 'Access-Control-Allow-Origin' header

Server did not include the CORS headers

Ensure server explicitly allows the requesting domain

Preflight request blocked

Server did not allow the method or headers in OPTIONSresponse

Add Access-Control-Allow-Methods and Access-Control-Allow-Headers

Credentials request blocked

Access-Control-Allow-Credentials is missing

Add Access-Control-Allow-Credentials: true and avoid * in Access-Control-Allow-Origin

Last updated

Was this helpful?