Skip to content

Fixed: speed module would cause fatal errors in wp-admin on multisite networks.#306

Open
Dan0sz wants to merge 1 commit into
developfrom
fix_speed_module_multisite
Open

Fixed: speed module would cause fatal errors in wp-admin on multisite networks.#306
Dan0sz wants to merge 1 commit into
developfrom
fix_speed_module_multisite

Conversation

@Dan0sz
Copy link
Copy Markdown
Collaborator

@Dan0sz Dan0sz commented May 28, 2026

Summary by CodeRabbit

  • Bug Fixes
    • Improved proxy request detection to gracefully handle cases where WordPress rewrite rules aren't initialized, preventing potential errors during early WordPress initialization phases.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

Plugin version bumped to 1.0.2. The is_proxy_request() function now checks whether the global $wp_rewrite object is initialized, returning false early if it is null, before attempting to compute the expected REST URL path.

Changes

Proxy Request Detection Hardening

Layer / File(s) Summary
Plugin version and proxy detection guard
mu-plugin/plausible-proxy-speed-module.php
Version updated to 1.0.2. Added null check for $wp_rewrite global in is_proxy_request() to safely handle cases where WordPress rewrite rules are not yet initialized, with early return and updated docblock.

Possibly Related PRs

  • plausible/wordpress#297: Both PRs modify the proxy detection logic in mu-plugin/plausible-proxy-speed-module.php's is_proxy_request() function to harden rewrite rule handling.

Poem

🐰 A guard clause hops in place,
Where $wp_rewrite shows its face—
Null check stops the fatal fall,
Version bumps through it all.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions fixing fatal errors on multisite networks, but the actual change is a version bump and a proxy-request detection fix to handle uninitialized rewrite rules—not specifically a multisite-specific fix. Revise the title to accurately reflect the core change: something like 'Fixed: proxy-request detection when WordPress rewrite rules are not initialized' would better represent the actual technical change.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix_speed_module_multisite

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mu-plugin/plausible-proxy-speed-module.php`:
- Around line 71-80: The current guard and docblock overstate rest_url()'s
dependency on $wp_rewrite and can cause false negatives; update the comment to
note that $wp_rewrite is only needed in specific multisite/permalink cases (via
get_rest_url()/rest_url()), and change the early-return to only bail when
$wp_rewrite is null AND one of those conditions applies (e.g., is_multisite() or
a non-empty get_option('permalink_structure')); reference get_rest_url(),
rest_url(), and the global $wp_rewrite in the updated comment and change the if
to: if ( $wp_rewrite === null && ( is_multisite() ||
get_option('permalink_structure') ) ) return false;
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 52a8a81a-dc9a-4c97-989f-92c1fad1b3f8

📥 Commits

Reviewing files that changed from the base of the PR and between c2fc480 and 27f5a20.

📒 Files selected for processing (1)
  • mu-plugin/plausible-proxy-speed-module.php

Comment on lines +71 to +80
/**
* @see rest_url() requires $wp_rewrite to be set. If it's not set yet, just assume this isn't a proxy request.
*
* @since v1.0.2
*/
global $wp_rewrite;

if ( $wp_rewrite === null ) {
return false;
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 28, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify the relationship between rest_url() and $wp_rewrite in WordPress core

# Search for rest_url implementation and $wp_rewrite usage
rg -n -C5 'function rest_url' --type=php

# Check if there's any documentation about this dependency in WordPress core or comments
rg -n '$wp_rewrite.*rest_url|rest_url.*wp_rewrite' --type=php

Repository: plausible/wordpress

Length of output: 225


🌐 Web query:

Does WordPress rest_url() function require $wp_rewrite global to be initialized?

💡 Result:

No. WordPress’s rest_url (implemented via get_rest_url / rest_url in wp-includes/rest-api.php) does not require the global $wp_rewrite to already be initialized. Evidence: - The rest_url helper simply calls get_rest_url(null, $path, $scheme) [1]. - In get_rest_url, $wp_rewrite is only used inside the conditional branch for multisite when permalink_structure is enabled; it is referenced as a global $wp_rewrite in that branch [2][3]. - In other cases (non-multisite or permalink_structure not enabled), get_rest_url builds the URL without using $wp_rewrite at all [2][3]. - Therefore, if you are in a context where that multisite/permalinks branch is taken and $wp_rewrite is null/uninitialized, then get_rest_url would need it (so you’d want to ensure rewrite init before calling in that specific scenario). But there is no general “rest_url requires $wp_rewrite to be initialized” requirement across all call sites; the dependency is conditional [2][3]. Practical guidance: - If you’re on multisite with permalinks enabled (permalink_structure), make sure WordPress has set up $wp_rewrite before calling rest_url/get_rest_url. - Otherwise (single site, or multisite without permalink_structure), rest_url should not depend on $wp_rewrite [2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '55,95p' mu-plugin/plausible-proxy-speed-module.php | cat -n

Repository: plausible/wordpress

Length of output: 1271


Refine the $wp_rewrite guard and docblock

rest_url() (via get_rest_url()) doesn’t generally require the global $wp_rewrite to be initialized—$wp_rewrite is only used in a conditional multisite/permalink_structure path. As written, returning early when $wp_rewrite === null could cause false negatives, and the current @see ... requires $wp_rewrite wording is too strong; narrow the guard or update the comment to match the conditional dependency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mu-plugin/plausible-proxy-speed-module.php` around lines 71 - 80, The current
guard and docblock overstate rest_url()'s dependency on $wp_rewrite and can
cause false negatives; update the comment to note that $wp_rewrite is only
needed in specific multisite/permalink cases (via get_rest_url()/rest_url()),
and change the early-return to only bail when $wp_rewrite is null AND one of
those conditions applies (e.g., is_multisite() or a non-empty
get_option('permalink_structure')); reference get_rest_url(), rest_url(), and
the global $wp_rewrite in the updated comment and change the if to: if (
$wp_rewrite === null && ( is_multisite() || get_option('permalink_structure') )
) return false;

Copy link
Copy Markdown
Collaborator Author

@Dan0sz Dan0sz May 28, 2026

Choose a reason for hiding this comment

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

You're incorrect here. In wp-includes/rest-api.php on line 518 and 520, it says:

global $wp_rewrite;

if ( $wp_rewrite->using_index_permalinks() ) {

Since the MU plugin initializes first, in some cases, $wp_rewrite isn't initialized yet. Throwing an error when using_index_permalinks() is triggered on null.

An alternative would be to intialize $wp_rewrite, like this:

if ($wp_rewrite === null) {
    include_once('some-file.php');
}

But, which file would I load there?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find wp-includes/rest-api.php around lines 515-525 to confirm the $wp_rewrite usage
# (using a vendor/wp copy if present, otherwise search for WP_Rewrite class definition)
fd -t f 'rest-api.php' | head -5
fd -t f 'class-wp-rewrite.php' | head -5
# Also check if there's a vendor WordPress copy
find . -path '*/wp-includes/rest-api.php' 2>/dev/null | head -5
find . -path '*/wp-includes/class-wp-rewrite.php' 2>/dev/null | head -5

Length of output: 1998


🌐 Web query:

WordPress WP_Rewrite class file location wp-includes/class-wp-rewrite.php get_rest_url using_index_permalinks

Length of output: 19011



@Dan0sz, you're absolutely right — I apologize for the incorrect analysis. The WordPress core source confirms exactly what you described. In get_rest_url() (wp-includes/rest-api.php):

if ( is_multisite() && get_blog_option( $blog_id, 'permalink_structure' ) || get_option( 'permalink_structure' ) ) {
    global $wp_rewrite;

    if ( $wp_rewrite->using_index_permalinks() ) {  // Fatal if $wp_rewrite is null

There is no null check before calling ->using_index_permalinks(), so your guard is both necessary and correct.


On the "which file" question: WP_Rewrite lives in wp-includes/class-wp-rewrite.php, so the include would be:

if ( $wp_rewrite === null ) {
    require_once ABSPATH . WPINC . '/class-wp-rewrite.php';
    $wp_rewrite = new WP_Rewrite();
}

However, I'd recommend against this approach for a couple of reasons:

  1. WP_Rewrite::init() reads the permalink_structure option from the database, and at the MU plugin initialization stage, the DB connection may not yet be fully bootstrapped.
  2. WordPress will instantiate $wp_rewrite itself shortly after anyway (in wp-settings.php), which could conflict with or overwrite your early instance.

Your current fix — returning false early when $wp_rewrite === null — is the cleanest and safest solution. In that edge case, the speed module simply skips filtering (no optimization), which is a much better outcome than a fatal error. The current implementation is correct as-is.


🧬 Code Graph Analysis Results

mu-plugin/plausible-proxy-speed-module.php

Relevant snippet (initializes during MU plugin load and calls is_proxy_request()): lines 33-38 + 128

public function __construct() {
		$this->request_uri      = $this->get_request_uri();
		$this->is_proxy_request = $this->is_proxy_request();

		$this->init();
}

new PlausibleProxySpeed();

Relevant snippet (checks $wp_rewrite before calling rest_url()): lines 58-88

private function is_proxy_request() {
		$namespace = get_option( 'plausible_analytics_proxy_resources' )['namespace'] ?? '';

		if ( ! $namespace ) {
			return false;
		}

		$path = parse_url( $this->request_uri, PHP_URL_PATH );

		if ( ! is_string( $path ) || $path === '' ) {
			return false;
		}

		/**
		 * `@see`   rest_url() requires $wp_rewrite to be set. If it's not set yet, just assume this isn't a proxy request.
		 *
		 * `@since` v1.0.2
		 */
		global $wp_rewrite;

		if ( $wp_rewrite === null ) {
			return false;
		}

		$expected = function_exists( 'rest_url' )
			? untrailingslashit( (string) wp_parse_url( rest_url( trim( $namespace, '/' ) ), PHP_URL_PATH ) )
			: '/wp-json/' . trim( $namespace, '/' );

		return $path === $expected
		       || str_starts_with( $path, $expected . '/' );
}

✏️ Learnings added
Learnt from: Dan0sz
Repo: plausible/wordpress PR: 306
File: mu-plugin/plausible-proxy-speed-module.php:71-80
Timestamp: 2026-05-28T17:47:59.566Z
Learning: In `mu-plugin/plausible-proxy-speed-module.php` (Plausible Analytics WordPress plugin, PHP), `is_proxy_request()` guards against a `null` global `$wp_rewrite` before calling `rest_url()` / `get_rest_url()`. This is necessary because WordPress core's `get_rest_url()` (`wp-includes/rest-api.php`) calls `$wp_rewrite->using_index_permalinks()` without a null check when `permalink_structure` is set or on multisite. Since the MU plugin initializes before WordPress fully bootstraps, `$wp_rewrite` can be null at that point, causing a fatal PHP error. The correct fix is the early `return false` guard (not manually instantiating `WP_Rewrite`).

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

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.

1 participant