[wp-trac] [WordPress Trac] #65030: Customize: `isLinkPreviewable()` regex overrides `customize_allowed_urls` filter for sites with `/wp-content/` in home URL
WordPress Trac
noreply at wordpress.org
Tue Apr 7 07:42:11 UTC 2026
#65030: Customize: `isLinkPreviewable()` regex overrides `customize_allowed_urls`
filter for sites with `/wp-content/` in home URL
--------------------------+-----------------------------
Reporter: abhi3315 | Owner: (none)
Type: defect (bug) | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Customize | Version: 6.9.4
Severity: normal | Keywords:
Focuses: javascript |
--------------------------+-----------------------------
== Summary
The `customize_allowed_urls` PHP filter allows plugins and themes to
register additional previewable URLs for the Customizer. However, the
hardcoded regex in `isLinkPreviewable()` in `customize-preview.js`
overrides this filter for any URL whose pathname contains `/wp-content/` —
making the filter incomplete.
When a URL passes the `settings.url.allowed` check (populated by
`customize_allowed_urls`), it should be considered previewable. Instead,
the regex silently rejects it, and there is no JS-side filter or hook to
override this behavior.
== Steps to reproduce
1. Install WordPress in a subdirectory under `wp-content/` (e.g.,
`example.com/wp-content/subsite/`)
2. Confirm `home_url('/')` returns the `wp-content` based URL
3. Log in and open Appearance → Customize
4. The preview iframe fails to load
Note: `get_allowed_urls()` includes `home_url('/')` by default, so the
site's own URL is in the allowed list — yet the Customizer still rejects
it.
== Root cause
In `src/js/_enqueues/wp/customize/preview.js`, the `isLinkPreviewable()`
function runs two checks in sequence:
{{{#!js
// Check 1: URL matches an explicitly allowed URL — PASSES
matchesAllowedUrl = ! _.isUndefined( _.find( api.settings.url.allowed,
function( allowedUrl ) {
parsedAllowedUrl.href = allowedUrl;
return parsedAllowedUrl.protocol === element.protocol
&& parsedAllowedUrl.host === element.host
&& 0 === element.pathname.indexOf( parsedAllowedUrl.pathname );
} ) );
if ( ! matchesAllowedUrl ) {
return false;
}
// Check 2: Hardcoded regex — overrides check 1 for URLs containing /wp-
content/
if ( /\/wp-(admin|includes|content)(\/|$)/.test( element.pathname ) ) {
return false;
}
}}}
Check 2 does not consider whether the URL was already approved by check 1.
A URL can be explicitly allowed via the PHP filter and still be blocked by
the JS regex.
The same issue exists in the `previewUrl` setter in `customize-
controls.js`.
== Expected behavior
URLs that match an entry in `settings.url.allowed` (populated via the
`customize_allowed_urls` PHP filter) should not be overridden by the
hardcoded regex. The regex should only apply to URLs that did **not**
match the allowed list.
== Proposed fix
{{{#!js
// Only apply the regex block for URLs that were NOT explicitly allowed
if ( ! matchesAllowedUrl && /\/wp-(admin|includes|content)(\/|$)/.test(
element.pathname ) ) {
return false;
}
}}}
This is backward-compatible. If no allowed URL contains `/wp-content/`,
behavior is identical to today. It only changes behavior when the PHP
filter has explicitly approved a URL that happens to contain `/wp-
content/` in its path.
== Related tickets
- #38409 — Hardened URL matching in `isLinkPreviewable`
- #31517 — Added non-previewable link notice in Customizer
- #30937 — Customize changesets, introduced `isLinkPreviewable`
--
Ticket URL: <https://core.trac.wordpress.org/ticket/65030>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list