Normalize document_root() output after realpath() so Page Cache rewrite rules work on Windows - #1379
Normalize document_root() output after realpath() so Page Cache rewrite rules work on Windows#1379tivnet wants to merge 1 commit into
Conversation
…te rules work on Windows
`Util_Environment::document_root()` returns the raw output of `realpath()` at two of
its return points without normalizing the directory separators. On Windows `realpath()`
returns back-slashes (and a drive letter), so the returned path does not match the
forward-slash-normalized cache directory that other code compares it against. As a
result `PgCache_Environment::apache_cache_uri_path()` fails to relativize the cache
path and falls back to the absolute path, producing a broken Disk: Enhanced serve rule.
This makes `document_root()` consistent with its sibling `site_root()` in the same
file, which already normalizes after `realpath()`.
### Symptom (Disk: Enhanced on Windows)
The generated `.htaccess` serve rule ends up glueing `%{DOCUMENT_ROOT}` onto an
absolute filesystem path, for example:
```
RewriteCond "%{DOCUMENT_ROOT}C:/path/to/site/wp-content/cache/page_enhanced/%{HTTP_HOST}/.../_index....html" -f
```
The `-f` test can never succeed against that path, so every request falls through to
PHP (advanced-cache.php) instead of being served statically. Page caching still
produces correct files, but the static-serve fast path is silently lost.
### Root cause
`site_root()` normalizes after `realpath()`:
```php
public static function site_root() {
$site_root = ABSPATH;
$site_root = realpath( $site_root );
$site_root = self::normalize_path( $site_root ); // normalized
return $site_root;
}
```
`document_root()` does not - it returns the raw `realpath()` result. On Windows that
value contains back-slashes, so downstream string comparisons against the
forward-slash cache directory (in `apache_cache_uri_path()`) miss, and the code falls
back to emitting the absolute path.
### Fix
Add `self::normalize_path()` before each return in `document_root()`, so the function
always returns a forward-slash path regardless of platform - matching `site_root()`.
### Testing
- Linux is unaffected: `realpath()` already returns forward slashes, so
`normalize_path()` is a no-op there and the emitted rewrite rules are unchanged.
- Windows: the emitted Disk: Enhanced serve `RewriteCond` becomes web-root-relative
(`%{DOCUMENT_ROOT}/wp-content/cache/page_enhanced/...`), and Apache serves the
cached file statically.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before autofix could start.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 14aadad. Configure here.
| if ( substr( $script_filename, -strlen( $php_self ) ) === $php_self ) { | ||
| $document_root = substr( $script_filename, 0, -strlen( $php_self ) ); | ||
| $document_root = realpath( $document_root ); | ||
| $document_root = self::normalize_path( $document_root ); |
There was a problem hiding this comment.
CDN path stripping breaks on Windows
Medium Severity
document_root() now always returns a forward-slash path after realpath(), which fixes Page Cache comparisons, but several CDN callers still str_replace that value against OS-native paths such as WP_CONTENT_DIR and upload basedir without normalizing the other side. On Windows those prefixes no longer match, so folder-placeholder and relative-path stripping can leave absolute paths in place.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 14aadad. Configure here.


Util_Environment::document_root()returns the raw output ofrealpath()at two of its return points without normalizing the directory separators. On Windowsrealpath()returns back-slashes (and a drive letter), so the returned path does not match the forward-slash-normalized cache directory that other code compares it against. As a resultPgCache_Environment::apache_cache_uri_path()fails to relativize the cache path and falls back to the absolute path, producing a broken Disk: Enhanced serve rule.This makes
document_root()consistent with its siblingsite_root()in the same file, which already normalizes afterrealpath().Symptom (Disk: Enhanced on Windows)
The generated
.htaccessserve rule ends up glueing%{DOCUMENT_ROOT}onto an absolute filesystem path, for example:The
-ftest can never succeed against that path, so every request falls through to PHP (advanced-cache.php) instead of being served statically. Page caching still produces correct files, but the static-serve fast path is silently lost.Root cause
site_root()normalizes afterrealpath():document_root()does not - it returns the rawrealpath()result. On Windows that value contains back-slashes, so downstream string comparisons against the forward-slash cache directory (inapache_cache_uri_path()) miss, and the code falls back to emitting the absolute path.Fix
Add
self::normalize_path()before each return indocument_root(), so the function always returns a forward-slash path regardless of platform - matchingsite_root().Testing
realpath()already returns forward slashes, sonormalize_path()is a no-op there and the emitted rewrite rules are unchanged.RewriteCondbecomes web-root-relative (%{DOCUMENT_ROOT}/wp-content/cache/page_enhanced/...), and Apache serves the cached file statically.Note
Low Risk
Small, platform-targeted path normalization in environment helpers; Linux paths are unchanged and the change only affects how document root strings are formatted for comparisons and rewrite rules.
Overview
Util_Environment::document_root()now runsnormalize_path()on every return path, including afterrealpath()and on thedocroot_fixbranch. That aligns it withsite_root(), which already normalized post-realpath()output.On Windows, raw
realpath()backslashes broke string comparisons against forward-slash cache paths in Disk: Enhanced rewrite generation (e.g.PgCache_Environment::apache_cache_uri_path()), so.htaccessRewriteCondpaths stayed absolute and static serve never matched. Linux behavior is unchanged because normalization is effectively a no-op when paths already use forward slashes.Reviewed by Cursor Bugbot for commit 14aadad. Bugbot is set up for automated code reviews on this repo. Configure here.