[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