Mastering Cross-Origin: From JSONP to CORS and Beyond
This article explains the browser's same‑origin policy, why cross‑origin requests are blocked, and walks through practical solutions—including JSONP, CORS with simple and preflight requests, PostMessage, WebSocket, Nginx reverse proxy, Node middleware proxy, and document.domain—so developers can choose the right technique for their needs.
Today we discuss the often‑repeated topic of cross‑origin requests and how to handle them effectively.
What is "cross‑source"?
The term refers to the browser's same‑origin policy, which restricts requests to resources that share the same protocol, domain, and port. For example, the URL https://www.google.com:3000 has a different origin because the protocol (https), domain (www.google.com), and port (3000) differ from the current page.
When origins differ, browsers block access to cookies, localStorage, IndexedDB, DOM nodes, and may intercept Ajax responses for security reasons, preventing cookie theft, XSS/CSRF attacks, and malicious file execution.
JSONP
JSONP exploits the fact that the <script> tag's src attribute is not subject to same‑origin restrictions. The server wraps JSON data in a callback function, which the client defines to receive the data.
Create a <script> element pointing to the cross‑origin URL.
Provide a callback function name via query parameters.
The server returns callback({...}).
function jsonp({ url, params, callback }) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
window[callback] = data => {
resolve(data);
document.body.removeChild(script);
};
const formatParams = { ...params, callback };
const requestParams = Object.keys(formatParams)
.map(k => `${k}=${formatParams[k]}`)
.join('&');
const src = `${url}?${requestParams}`;
script.setAttribute('src', src);
document.body.appendChild(script);
});
}
// usage
jsonp({
url: 'https://example.com',
params: {...},
callback: 'func'
});JSONP is simple and highly compatible but only works with GET requests and requires server support.
CORS
CORS (Cross‑Origin Resource Sharing) uses additional HTTP headers to tell the browser that a particular origin is allowed to access the resource. For simple requests (GET, HEAD, POST with specific content‑types), the browser adds an Origin header and expects Access-Control-Allow-Origin in the response.
For non‑simple requests, the browser first sends a preflight OPTIONS request containing Access-Control-Request-Method and Access-Control-Request-Headers. The server must respond with appropriate Access-Control-Allow‑* headers; otherwise the request is blocked.
GET /cors HTTP/1.1
Origin: https://example.com
... OPTIONS /cors HTTP/1.1
Origin: http://example.com
Access-Control-Request-Method: PUT
...Simple requests are those using GET, HEAD, or POST with Content‑Type of text/plain, multipart/form-data, or application/x-www-form-urlencoded. All other requests trigger a preflight.
PostMessage
PostMessage, part of the HTML5 XMLHttpRequest Level 2 API, enables cross‑document messaging between windows, iframes, Web Workers, and Service Workers. It works like a publish‑subscribe pattern, avoiding direct references between windows.
window.postMessage(message, origin);
window.addEventListener("message", function receiveMessage(event) {
// event.source, event.origin, event.data
}, false);WebSocket
WebSocket provides a persistent full‑duplex connection between browser and server, allowing bidirectional data flow and serving as another cross‑origin solution.
const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
ws.send('something');
});
ws.on('message', function incoming(data) {
console.log(data);
});Note that multiple WebSocket connections on a single page can affect performance.
Nginx Reverse Proxy
Since server‑to‑server communication is not limited by the browser's same‑origin policy, an Nginx reverse proxy can forward client requests to another server, effectively bypassing cross‑origin restrictions.
# nginx.conf
server {
listen 80;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080;
proxy_cookie_domain www.domain2.com www.domain1.com;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true;
}
}Node Middleware Proxy
A Node server can act as a proxy, adding CORS headers before forwarding the request to the target server.
const https = require('https');
const server = https.createServer((req, res) => {
const { method, headers } = req;
res.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Headers': 'Content-Type'
});
const proxy = https.request({ host: 'target.com', method, headers }, response => {
let body = '';
response.on('data', chunk => body += chunk);
response.on('end', () => res.end(body));
});
proxy.end();
});document.domain
When subdomains share the same parent domain (e.g., a.test.com and b.test.com), setting document.domain = 'test.com' allows them to access each other's DOM.
document.domain = 'test.com';
const iframe = document.createElement('iframe');
iframe.setAttribute('src', 'b.test.com/xxx.html');
iframe.onload = function() {
console.log(iframe.contentWindow.xxx);
};
document.appendChild(iframe);Summary
CORS supports all HTTP methods and is the most mainstream solution.
JSONP works only with GET but offers wide browser compatibility.
Server‑side proxies (Nginx, Node) bypass same‑origin limits because they operate outside the browser.
WebSocket provides a full‑duplex cross‑origin channel.
PostMessage enables cross‑document communication, ideal for window‑to‑window messaging.
document.domain, window.name, and location.hash are legacy techniques gradually being replaced by PostMessage.
Final Note
This article was originally published on the author's blog. Feedback and corrections are welcome.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
