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.

Programmer DD
Programmer DD
Programmer DD
Mastering Cross-Origin: From JSONP to CORS and Beyond

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendCORSCross-OriginWeb SecurityJSONP
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.