Browser Cross-Origin Restrictions and Practical Workarounds

Web browsers enforce the Same-Origin Policy to prevent scripts from one origin interacting with resources from another. An origin is defined by matching scheme, host, and port. While cross-origin embedding (e.g., via <script>, <img>, <iframe>) is permitted, JavaScript is restricted from reading or manipulating the loaded content.

Common Restrictions

  • XMLHttpRequest and Fetch API cannot send cross-origin requests without server approval.
  • Cross-origin cookies and Web Storage (localStorage/sessionStorage) are inaccessible.
  • DOM access across origins in iframes is blocked unless explicitly allowed.

Workaround 1: JSONP via Script Injection

Leverage the fact that <script> tags bypass CORS. Inject a script tag with a callback parameter. The server wraps data in the named function.

// Client-side
function handleResponse(payload) {
    console.log(payload);
}

const script = document.createElement('script');
script.src = 'http://localhost/api?cb=handleResponse';
document.body.appendChild(script);

// Server-side (PHP example)
$data = ['status' => 'ok', 'items' => [1, 2, 3]];
$cb = $_GET['cb'] ?? 'callback';
echo $cb . '(' . json_encode($data) . ');';

Workaround 2: CORS Headers

Configure the server to respond with appropriate CORS headers. Simple requests (GET/POST with basic headers) trigger no preflight. Complex requests require an OPTIONS prfelight.

// Server response headers
Access-Control-Allow-Origin: https://trusted-site.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true

Workaround 3: Reverse Proxy with Nginx

Route cross-origin requests through a same-origin proxy endpoint.

location /api/remote {
    proxy_pass http://backend-server:3000/;
    proxy_set_header Host $host;
}

Workaround 4: Image Beacon (GET-only)

Use image tags for fire-and-forget tracking. Limited to GET and no response handling.

<img src="https://tracker.com/pixel?user=123&event=view" alt="" width="1" height="1">

Workaround 5: Shared Domain via document.domain

For subdomains under the same root (e.g., app.site.com and api.site.com), relax policy by setting:

document.domain = "site.com";
// Now iframe.contentWindow is accessible

Workaround 6: window.name Transport

Exploit persistent window.name property across page navigations within an iframe.

// Inside iframe
window.name = JSON.stringify({ token: 'abc123' });

// Parent reads after iframe reloads to same-origin page
const data = JSON.parse(iframe.contentWindow.name);

Workaround 7: postMessage API

Securely exchange messages between windows or iframes regardless of origin. Always validate event.origin.

// Sender
targetWindow.postMessage({ action: 'update' }, 'https://receiver.com');

// Receiver
window.addEventListener('message', (event) => {
    if (event.origin !== 'https://sender.com') return;
    console.log('Received:', event.data);
});

Tags: cors JSONP nginx postMessage web-security

Posted on Tue, 27 Oct 2026 16:42:32 +0000 by Karlos94