Mastering CORS: In‑Depth Java Interview Guide for Cross‑Origin Solutions
This comprehensive guide explains the browser same‑origin policy, why cross‑origin restrictions exist, and walks through five major solutions—CORS, proxy servers, JSONP, postMessage, and WebSocket—detailing their principles, advantages, drawbacks, configuration examples, and best‑practice recommendations for interview scenarios.
Background
Web browsers enforce the same‑origin policy to protect user data. Two URLs are considered same‑origin only when their protocol, domain, and port are identical. Because AJAX (XMLHttpRequest/fetch) can read the response body, the browser blocks cross‑origin AJAX calls unless the server explicitly permits them.
Solution Overview
CORS (Cross‑Origin Resource Sharing) – a W3C standard where the server adds Access-Control-* response headers. Works for all HTTP methods, supports credentials, and is the preferred production‑grade solution.
Proxy Server – the browser sends the request to a same‑origin development server, which forwards it to the real backend. No changes to the backend are required; useful for local development.
JSONP (JSON with Padding) – exploits the fact that <script> tags are not subject to same‑origin checks. The client supplies a callback name; the server returns JavaScript that invokes the callback with the data. Only works for GET requests and has security drawbacks.
postMessage – enables safe communication between windows or iframes from different origins. Suitable when the cross‑origin interaction is limited to UI embedding.
WebSocket – establishes a full‑duplex connection that is not blocked by the same‑origin policy. The server must still validate the Origin header, but browsers do not block the handshake.
Detailed CORS Mechanics
Simple request – if the request meets a small set of criteria (e.g., GET/POST with simple headers), the browser sends it directly and checks the Access-Control-Allow-Origin header in the response.
Preflight request – for methods like PUT/DELETE or when custom headers are used, the browser first sends an OPTIONS request. The server must respond with Access-Control-Allow-Methods, Access-Control-Allow-Headers, and optionally Access-Control-Max-Age to cache the decision.
Common CORS Response Headers
Access-Control-Allow-Origin– specifies the allowed origin (e.g., * or http://localhost:3000). Access-Control-Allow-Methods – list of HTTP methods the server permits (e.g., GET, POST, PUT, DELETE, OPTIONS). Access-Control-Allow-Headers – list of request headers the server accepts (e.g., Content-Type, X-Token). Access-Control-Allow-Credentials – true if cookies or HTTP authentication should be included. Access-Control-Max-Age – number of seconds the preflight result may be cached (e.g., 3600). Access-Control-Expose-Headers – response headers that JavaScript is allowed to read (e.g., X-Total-Count).
Spring Boot CORS Configuration Example
// Global configuration (recommended)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOriginPatterns("*") // SpringBoot 2.4+ uses patterns
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
// Filter‑based configuration (more flexible)
@Component
public class CorsFilter implements Filter {
@Override
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");
if (origin != null && isAllowedOrigin(origin)) {
response.setHeader("Access-Control-Allow-Origin", origin);
}
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Max-Age", "3600");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(req, res);
}
}Proxy Server Approach
Because the same‑origin restriction only applies to the browser, a local development server can act as a proxy and forward API calls to the real backend.
Vite Proxy Configuration
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:8080', // backend address
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
}Nginx Reverse Proxy (Production)
server {
listen 80;
server_name example.com;
# Serve static assets
location / {
root /var/www/html;
index index.html;
}
# API proxy
location /api/ {
proxy_pass http://backend-server:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}JSONP
JSONP bypasses same‑origin checks by loading a <script> tag whose src points to the API endpoint with a callback query parameter. The server returns JavaScript that calls the supplied function.
Client‑side Implementation
function jsonp(url, callback) {
const cbName = 'jsonp_' + Date.now();
window[cbName] = function(data) {
callback(data);
delete window[cbName];
document.body.removeChild(script);
};
const script = document.createElement('script');
script.src = `${url}?callback=${cbName}`;
document.body.appendChild(script);
}
// Usage
jsonp('http://api.example.com/user', function(data) {
console.log(data);
});Limitations : only GET, vulnerable to XSS if the backend is compromised, and HTTP status codes are not exposed for error handling.
postMessage (Cross‑Window Communication)
When an iframe or a popup from another origin needs to exchange data with the parent page, window.postMessage provides a secure, origin‑checked channel. The receiver registers a message event listener and validates event.origin before processing the payload.
WebSocket
WebSocket connections are established via an HTTP upgrade handshake that includes an Origin header. Browsers do not block the connection; instead, the server should verify the Origin value to prevent unauthorized clients. After the handshake, communication is full‑duplex and not subject to CORS.
Cross‑Origin Requests with Cookies
To include cookies or HTTP authentication in a cross‑origin request, the client must opt‑in and the server must allow credentials.
Client side
// fetch API
fetch('http://api.example.com/user', {
credentials: 'include' // send cookies
});
// axios
axios.get('http://api.example.com/user', { withCredentials: true });Server side (CORS headers)
response.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000'); // must be explicit, not '*'
response.setHeader('Access-Control-Allow-Credentials', 'true');Additionally, the cookie itself must be set with SameSite=None; Secure and served over HTTPS for the browser to send it cross‑origin.
Choosing the Right Approach
Production services – use CORS with properly configured headers; combine with a reverse proxy (e.g., Nginx) if you need to terminate TLS or perform load balancing.
Local development – a proxy server (Vite, Webpack dev server, or Nginx) avoids CORS configuration on the backend.
Legacy systems that only support GET – JSONP can be a quick fix, but be aware of security and error‑handling drawbacks.
Embedding third‑party UI – use postMessage for safe window communication.
Real‑time bidirectional traffic – WebSocket is appropriate; still validate the Origin header on the server.
Performance Tips for CORS
Cache preflight responses with Access-Control-Max-Age to reduce extra round‑trips.
Avoid triggering preflight by using simple request headers (e.g., Content-Type: text/plain) when possible.
Ensure the server responds to OPTIONS requests quickly and without invoking business logic.
Java Architect Handbook
Focused on Java interview questions and practical article sharing, covering algorithms, databases, Spring Boot, microservices, high concurrency, JVM, Docker containers, and ELK-related knowledge. Looking forward to progressing together with you.
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.
