CORS

Same-Origin Policy

Browsers block JavaScript on one origin from reading responses from another origin. An origin is scheme + host + port. Your React app on http://localhost:5173 calling http://localhost:8080 is cross-origin, so the browser blocks it unless the API says it is allowed. That permission system is CORS (Cross-Origin Resource Sharing).

Example

Two URLs share an origin only if scheme, host and port all match:

import java.net.URI;

public class Main {
    static String origin(String url) {
        URI u = URI.create(url);
        int port = u.getPort() != -1 ? u.getPort() : (u.getScheme().equals("https") ? 443 : 80);
        return u.getScheme() + "://" + u.getHost() + ":" + port;
    }

    public static void main(String[] args) {
        String api = "https://api.shopease.in/products";
        String[] pages = {
            "https://api.shopease.in/docs",
            "https://shopease.in/cart",
            "http://api.shopease.in/products",
            "https://api.shopease.in:8443/products",
        };
        for (String page : pages) {
            boolean same = origin(page).equals(origin(api));
            System.out.printf("%-40s %s%n", page, same ? "same origin" : "CROSS-ORIGIN");
        }
    }
}

Output

https://api.shopease.in/docs             same origin
https://shopease.in/cart                 CROSS-ORIGIN
http://api.shopease.in/products          CROSS-ORIGIN
https://api.shopease.in:8443/products    CROSS-ORIGIN

Preflight Requests

Example

For non-simple requests (JSON body, Authorization header, PUT/DELETE) the browser first sends an OPTIONS preflight:

curl -i -X OPTIONS localhost:8080/api/orders \
  -H "Origin: http://localhost:5173" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: content-type,authorization"

Example output

HTTP/1.1 200
Access-Control-Allow-Origin: http://localhost:5173
Access-Control-Allow-Methods: GET,POST,PUT,DELETE
Access-Control-Allow-Headers: content-type, authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 3600

Enabling CORS With Spring Security

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .cors(Customizer.withDefaults())          // uses the CorsConfigurationSource bean below
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .httpBasic(Customizer.withDefaults());
    return http.build();
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowedOrigins(List.of("http://localhost:5173", "https://shopease.in"));
    config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
    config.setAllowedHeaders(List.of("Content-Type", "Authorization"));
    config.setAllowCredentials(true);
    config.setMaxAge(3600L);
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/api/**", config);
    return source;
}
  • Configure CORS in Spring Security (http.cors(...)), otherwise the preflight is rejected with 401 before it reaches your @CrossOrigin controller.
  • Never use "*" as an allowed origin together with credentials.
  • CORS is a browser protection. curl and Postman ignore it, and it does not replace authentication.