[wp-trac] [WordPress Trac] #64560: Speculative Loading API can expose server path when using a theme directory outside the document root
WordPress Trac
noreply at wordpress.org
Tue Jan 27 22:00:52 UTC 2026
#64560: Speculative Loading API can expose server path when using a theme directory
outside the document root
--------------------------+-----------------------------
Reporter: emrl | Owner: (none)
Type: defect (bug) | Status: new
Priority: normal | Milestone: Awaiting Review
Component: General | Version:
Severity: normal | Keywords:
Focuses: |
--------------------------+-----------------------------
For several years we have been using `register_theme_directory()` to
register a theme directory outside of the document root. I understand that
it is unconventional, but it has worked perfectly with no issues until
now. It was done to follow best practices to keep PHP code (functions.php,
classes, templates) above any public directories.
The Speculative Loading feature now excludes some base/default
directories, and does not allow a real way to modify them via
filter/action.
`WP_URL_Pattern_Prefixer::get_default_contexts()` has no filter.
`wp_speculation_rules_href_exclude_paths` filter specifically runs
''before'' those default contexts are added, so they cannot be removed.
`wp_load_speculation_rules` action does not allow modification of any
rules as `WP_Speculation_Rules` has no method for removing/modifying
rules, it can only fully replace them. So to remove the `template` or
`stylesheet` context, one would need to duplicate the entire main rule
conditions from `wp_get_speculation_rules()`, which is fragile at best.
Themes outside the document root is rare, but there doesn't seem to be
anything preventing that, and everything else in WordPress seems to run
just fine doing so.
A quick fix has been for us to use the filters `stylesheet_directory_uri`
and `template_directory_uri` to remove the absolute server paths, but this
still leaves a potentially valid and real WordPress page URL on the
excluded list.
Example of output below, the server path is exposed in the HTML:
`/home/USER/apps/APP_NAME/releases/485/theme-name/*`
{{{
<script type="speculationrules">
{
"prefetch": [
{
"source": "document",
"where": {
"and": [
{
"href_matches": "/*"
},
{
"not": {
"href_matches": [
"/wp-*.php",
"/wp-admin/*",
"/wp-content/uploads/*",
"/wp-content/*",
"/wp-content/plugins/*",
"/home/USER/apps/APP_NAME/releases/485/theme-name/*",
"/*\\?(.+)"
]
}
},
{
"not": {
"selector_matches": "a[rel~=\"nofollow\"]"
}
},
{
"not": {
"selector_matches": ".no-prefetch, .no-prefetch a"
}
}
]
},
"eagerness": "conservative"
}
]
}
</script>
}}}
--
Ticket URL: <https://core.trac.wordpress.org/ticket/64560>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list