close
Skip to content

[CORS Proxy] Fix FirewallInterferenceError on local dev server#3353

Merged
adamziel merged 5 commits intotrunkfrom
fix/cors-proxy-null-origin
Mar 7, 2026
Merged

[CORS Proxy] Fix FirewallInterferenceError on local dev server#3353
adamziel merged 5 commits intotrunkfrom
fix/cors-proxy-null-origin

Conversation

@adamziel
Copy link
Collaborator

@adamziel adamziel commented Mar 7, 2026

What it does

Fixes FirewallInterferenceError when using the CORS proxy during local development.

Rationale

In production, the CORS proxy lives at https://wordpress-playground-cors-proxy.net — a different domain. The browser sends Origin: https://playground.wordpress.net on every cross-origin request, the proxy recognizes it, and includes the X-Playground-Cors-Proxy marker header.

In dev, the CORS proxy is exposed at /cors-proxy/ on the same Vite dev server (127.0.0.1:4400). The browser treats this as a same-origin request and doesn't send an Origin header at all. Without a recognized origin, should_respond_with_cors_headers() returned false, the X-Playground-Cors-Proxy marker was omitted, and fetchWithCorsProxy threw FirewallInterferenceError.

Making the proxy cross-origin in dev isn't an option — the Vite proxy exists to avoid Chrome's Private Network Access restrictions.

Implementation

Adds is_local_dev_server() which checks php_sapi_name() === 'cli-server'. Both should_respond_with_cors_headers() and the Access-Control-Allow-Origin header use it: the dev server accepts any origin, and falls back to * when no Origin header is present. Production behavior is unchanged — unknown or missing origins are still rejected.

Also wraps is_private_ip() in function_exists() so e2e tests can override it to allow localhost upstream connections, and enables test.sh to actually run the test suite.

Testing instructions

cd packages/playground/php-cors-proxy
bash test.sh

WordPress Playground runs inside a sandboxed iframe, which makes the
browser send `Origin: null` (the literal string) with every cross-origin
request. The CORS proxy didn't recognize this as a valid origin, so it
omitted all CORS response headers including X-Playground-Cors-Proxy.
The client then threw FirewallInterferenceError, mistaking the missing
header for network firewall interference.

The fix adds 'null' to the default supported_origins list, alongside
the existing localhost and playground.wordpress.net entries. This keeps
it configurable – production deployments that override the list via
PLAYGROUND_CORS_PROXY_SUPPORTED_ORIGINS must include 'null' explicitly.

Also enables the existing test suite (test.sh was a no-op before) and
adds e2e tests that start the actual PHP proxy server, send requests
with various Origin headers, and verify CORS behavior end-to-end.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 7, 2026 08:40
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the PHP CORS proxy to treat the literal Origin: null (from sandboxed iframes) as an allowed origin, preventing missing CORS/identification headers and avoiding client-side FirewallInterferenceErrors. Adds/activates unit + end-to-end tests to validate Origin handling via real HTTP requests against a live PHP built-in server.

Changes:

  • Allow 'null' in should_respond_with_cors_headers() default supported origins.
  • Add e2e harness that starts an upstream mock + the proxy and asserts on CORS headers end-to-end.
  • Enable test.sh to run PHPUnit + e2e tests.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/playground/php-cors-proxy/cors-proxy-functions.php Accepts 'null' origin; makes is_private_ip() overridable for test routing.
packages/playground/php-cors-proxy/tests/ProxyFunctionsTests.php Adds unit coverage for should_respond_with_cors_headers() including 'null'.
packages/playground/php-cors-proxy/tests/e2e/cors-proxy-e2e-test.php New e2e test runner that boots servers and validates CORS headers.
packages/playground/php-cors-proxy/tests/e2e/proxy-test-router.php Test router to override private-IP blocking while exercising the real proxy.
packages/playground/php-cors-proxy/tests/e2e/upstream-mock-router.php Mock upstream endpoints for proxy e2e runs.
packages/playground/php-cors-proxy/test.sh Turns test script into an actual runner for unit + e2e.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

In production, the CORS proxy lives at a different domain
(wordpress-playground-cors-proxy.net), so the browser always sends an
Origin header on cross-origin requests. In dev, the CORS proxy is
accessed via a same-origin Vite proxy (/cors-proxy/), so the browser
doesn't send an Origin header at all. Without a recognized Origin, the
proxy omitted the X-Playground-Cors-Proxy marker, causing the client to
throw FirewallInterferenceError.

Detect the PHP built-in dev server (cli-server SAPI) and accept every
origin in that context. Use Access-Control-Allow-Origin: * as fallback
when no Origin header is present.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@adamziel adamziel changed the title [CORS Proxy] Accept Origin: null from sandboxed iframes [CORS Proxy] Fix FirewallInterferenceError on local dev server Mar 7, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@adamziel adamziel force-pushed the fix/cors-proxy-null-origin branch from 15330ad to dbe2966 Compare March 7, 2026 12:52
@adamziel adamziel merged commit ae17710 into trunk Mar 7, 2026
42 checks passed
@adamziel adamziel deleted the fix/cors-proxy-null-origin branch March 7, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants