← Debugging Guides

阅读中文版本

SecurityError — pushState Cannot Be Called on a Cross-Origin URL

Fix pushState SecurityError by keeping history URLs same-origin and passing only path, query, and hash, never a full external URL.

intermediatedebuggingbrowserhistorysecurity

Symptoms

  • The same-origin check failed at the pushState call
  • pushState works locally but throws behind a CDN or on a different host
  • The error names a URL with a different origin than the document

Possible causes

  • pushState is given a URL whose origin differs from the document
  • A relative base or trailing slash resolves to a different origin
  • The app is embedded and constructs a URL from the parent origin

Diagnosis steps

Compare the target with the document origin

console.log(location.origin); console.log(new URL(target, location.href).origin);

Interpretation

Different origins explain the SecurityError; pushState only accepts same-origin URLs.

Reproduce the call

git grep -n "pushState\|replaceState"

Interpretation

Auditing calls shows which one passes an absolute cross-origin URL.

Root cause

history.pushState and replaceState require the target URL to be same-origin with the document. Browsers raise SecurityError otherwise to prevent history manipulation across origins.

Fix

Pass only a same-origin path, query, and hash (for example "/users/1?tab=posts"), resolve relative URLs against location.href, and never push an external absolute URL.

Risk note

Manually rebuilding paths can drop the base path when the app is deployed under a subpath; derive the base from location.

Verify the fix

Navigate through the app and confirm the URL updates without error and back/forward work; assert the pushed URLs share location.origin.

Symptoms

When you hit “SecurityError — pushState Cannot Be Called on a Cross-Origin URL”, guessing wastes time. The guide below follows evidence: reproduce the symptom, run targeted diagnostics, identify the root cause, apply the fix, and verify it with concrete checks.

Observed symptoms include:

  • The same-origin check failed at the pushState call
  • pushState works locally but throws behind a CDN or on a different host
  • The error names a URL with a different origin than the document

Reproduce and Diagnose

Reproduce the failure in isolation before changing any code. Work through the checks below in order; each one narrows the search space.

Step 1: Compare the target with the document origin

console.log(location.origin); console.log(new URL(target, location.href).origin);

Different origins explain the SecurityError; pushState only accepts same-origin URLs.

Step 2: Reproduce the call

git grep -n "pushState\|replaceState"

Auditing calls shows which one passes an absolute cross-origin URL.

Likely contributors, in order of frequency:

  • pushState is given a URL whose origin differs from the document
  • A relative base or trailing slash resolves to a different origin
  • The app is embedded and constructs a URL from the parent origin

Root Cause

history.pushState and replaceState require the target URL to be same-origin with the document. Browsers raise SecurityError otherwise to prevent history manipulation across origins.

The Fix

Pass only a same-origin path, query, and hash (for example “/users/1?tab=posts”), resolve relative URLs against location.href, and never push an external absolute URL.

Watch out: Manually rebuilding paths can drop the base path when the app is deployed under a subpath; derive the base from location.

// ❌ cross-origin -> SecurityError
// history.pushState({}, '', 'https://evil.example.com/steal');

// ✅ same-origin path only
history.pushState({ page: 1 }, '', '/users/1?tab=posts');

// resolve relative to the current document origin
const next = new URL('/users/2', location.origin).pathname;
history.pushState({ page: 2 }, '', next);

Verify and Prevent

Navigate through the app and confirm the URL updates without error and back/forward work; assert the pushed URLs share location.origin.

Once verified, add a regression test that reproduces the original symptom so the failure cannot silently return.

This class of failure is usually environmental rather than pure logic, so capture the exact command, the environment variables, and the working directory in your bug report. Reproduce it in the smallest possible script, then widen the scope only once the minimal case is understood. A short written checklist for this failure mode saves hours the next time it appears.