Opened 5 weeks ago
Last modified 4 weeks ago
#63111 new enhancement
short-circuit WordPress processing for requests of missing files
Reported by: |
|
Owned by: | |
---|---|---|---|
Milestone: | Awaiting Review | Priority: | normal |
Severity: | major | Version: | trunk |
Component: | Bootstrap/Load | Keywords: | changes-requested has-dev-note has-patch |
Focuses: | performance | Cc: |
Description
Any broken link in the HTML output by WordPress, or within a referenced CSS file, triggers WordPress to run again—completely.
If the missing file is an image used multiple times (such as a CSS background or sprite), WordPress will execute an additional process for each instance. This means a single missing image could cause WordPress to run multiple extra times on every page load, significantly impacting performance.
This issue was first reported 14 years ago (#17246), yet it remains unresolved. That is unacceptable.
It should not be the responsibility of plugin developers to patch this issue. Nor should site owners be forced to implement workarounds in their .htaccess files.
This is a core issue, and it should be addressed at the core level.
To help resolve this, I have implemented a new function that detects whether an incoming request is for a missing file before WordPress loads unnecessarily. I have also modified the template-loader.php file to utilize the new function and mitigate redundant executions. Perhaps there is a better place for the check to be placed.
The new function is akin to already-existing functions like wp_doing_ajax , wp_is_serving_rest_request and wp_is_json_request.
Steps to demonstrate the problem:
1) Set up a WordPress site and ensure debug logging is enabled. My wp-config.php file has the following entries:
@ini_set('log_errors', 1); @ini_set('error_log', dirname(__FILE__) . '/php-error.log'); // Set custom log file define( 'WP_DEBUG', true ); define( 'WP_DEBUG_DISPLAY', false ); define( 'WP_DEBUG_LOG', true );
2) Add the following PHP code to the top of wp-includes/functions.php
function wp_is_file_request($extension = '') { $is_file_request = false; if (empty($extension)) { $url = $_SERVER['REQUEST_URI'] ?? ''; $path = parse_url($url, PHP_URL_PATH) ?? ''; $extension = pathinfo($path, PATHINFO_EXTENSION); } if (!empty($extension)) { $ext = strtolower($extension); $mimes = apply_filters('wp_is_file_request_mime_types', wp_get_mime_types()); $ext_list = []; foreach ($mimes as $key => $value) { $ext_list = array_merge($ext_list, explode('|', $key)); } $is_file_request = in_array($ext, $ext_list, true); } return apply_filters('wp_is_file_request', $is_file_request); }
3) Add the following code to the top of wp-includes/template-loader.php:
error_log('Loading WordPress: before call to wp_is_file_request'); // Check if the request is for a file if (wp_is_file_request()) { // Capture the requested URL or path $url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : 'Unknown URL'; // Log that the file request returned true and the requested file error_log('Loading WordPress: call to wp_is_file_request returned true. Requested file: ' . $url); } error_log('Loading WordPress: after call to wp_is_file_request');
4) Open your browser and visit any page or post on your WordPress site.
5) Check if wp-content/debug.log exists. Open it and see if any entries appear in it. You should see something similar to the following:
[16-Mar-2025 04:28:53 UTC] Loading WordPress: before call to wp_is_file_request [16-Mar-2025 04:28:53 UTC] Loading WordPress: after call to wp_is_file_request
This indicates that WordPress ran only one time.
6) Open the page in the editor and add an img tag the points to a non-existent image file.
<img src="https://google.com/wp-admin/images/none.jpg" alt="" />
7) Clear debug.log of all entries and save.
8) View the page you just edited in the browser.
9) Open debug.log and see if any entries appear in it. You should see something similar to the following:
[16-Mar-2025 04:27:23 UTC] Loading WordPress: before call to wp_is_file_request [16-Mar-2025 04:27:23 UTC] Loading WordPress: after call to wp_is_file_request [16-Mar-2025 04:27:24 UTC] Loading WordPress: before call to wp_is_file_request [16-Mar-2025 04:27:24 UTC] Loading WordPress: call to wp_is_file_request returned true. Requested file: /wp-admin/none.jpg [16-Mar-2025 04:27:24 UTC] Loading WordPress: after call to wp_is_file_request
This indicates WordPress ran twice.
10) Add the following PHP code the theme's functions.php file:
function enqueue_non_existent_files() { // Enqueue a non-existent script file wp_enqueue_script('non-existent-js', get_template_directory_uri() . '/js/non-existent.js', array(), null, true); // Enqueue a non-existent CSS file wp_enqueue_style('non-existent-css', get_template_directory_uri() . '/css/non-existent.css'); } add_action('wp_enqueue_scripts', 'enqueue_non_existent_files');
11) Clear debug.log of all entries and save.
12) Re-load the page you previously edited in the browser.
13) Open debug.log and see if any entries appear in it. You should see something similar to the following:
[16-Mar-2025 04:24:04 UTC] Loading WordPress: before call to wp_is_file_request [16-Mar-2025 04:24:04 UTC] Loading WordPress: after call to wp_is_file_request [16-Mar-2025 04:24:05 UTC] Loading WordPress: before call to wp_is_file_request [16-Mar-2025 04:24:05 UTC] Loading WordPress: call to wp_is_file_request returned true. Requested file: /wp-content/themes/astra/css/non-existent.css?ver=1552ecb53cbdcbcbf181a26569fa7272 [16-Mar-2025 04:24:05 UTC] Loading WordPress: after call to wp_is_file_request [16-Mar-2025 04:24:05 UTC] Loading WordPress: before call to wp_is_file_request [16-Mar-2025 04:24:05 UTC] Loading WordPress: call to wp_is_file_request returned true. Requested file: /wp-content/themes/astra/js/non-existent.js [16-Mar-2025 04:24:05 UTC] Loading WordPress: after call to wp_is_file_request [16-Mar-2025 04:24:05 UTC] Loading WordPress: before call to wp_is_file_request [16-Mar-2025 04:24:05 UTC] Loading WordPress: call to wp_is_file_request returned true. Requested file: /wp-admin/none.jpg [16-Mar-2025 04:24:05 UTC] Loading WordPress: after call to wp_is_file_request [16-Mar-2025 04:24:07 UTC] Loading WordPress: before call to wp_is_file_request [16-Mar-2025 04:24:07 UTC] Loading WordPress: call to wp_is_file_request returned true. Requested file: /robots.txt [16-Mar-2025 04:24:07 UTC] Loading WordPress: after call to wp_is_file_request [16-Mar-2025 04:24:08 UTC] Loading WordPress: before call to wp_is_file_request [16-Mar-2025 04:24:08 UTC] Loading WordPress: call to wp_is_file_request returned true. Requested file: /robots.txt [16-Mar-2025 04:24:08 UTC] Loading WordPress: after call to wp_is_file_request [16-Mar-2025 04:24:09 UTC] Loading WordPress: before call to wp_is_file_request [16-Mar-2025 04:24:09 UTC] Loading WordPress: after call to wp_is_file_request [16-Mar-2025 04:24:10 UTC] Loading WordPress: before call to wp_is_file_request [16-Mar-2025 04:24:10 UTC] Loading WordPress: after call to wp_is_file_request
This indicates WordPress ran eight times. As you can see, the attempt to load either the non-existent js file or the non-existent css file causes the WP robots.txt virtual file functionality to fail, as well.
Here is an example of using the 'wp_is_file_request_mime_types' filter:
<?php // Make an exception for audio files add_filter('wp_is_file_request_mime_types', 'rdp_mime_types_filter'); function custom_mime_types_filter($mimes){ unset($mimes['mp3|m4a|m4b']); unset($mimes['ra|ram']); unset($mimes['wav']); unset($mimes['ogg|oga']); unset($mimes['mid|midi']); unset($mimes['wma']); unset($mimes['wax']); unset($mimes['mka']); return $mimes; }
Here is an example of using the 'wp_is_file_request' filter:
<?php function custom_wp_is_file_request_filter($is_file_request) { // Get the requested URL path $url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; // Extract the requested file name $path = parse_url($url, PHP_URL_PATH); $filename = basename($path); // If the requested file is 'my-special-message.wav', allow WordPress to process it if ($filename === 'my-special-message.wav') { return false; // Override and allow full WordPress processing } // Keep the original decision for all other files return $is_file_request; } add_filter('wp_is_file_request', 'custom_wp_is_file_request_filter');
Here's an example of checking if the request is for a wav file:
<?php $isWAV = wp_is_file_request('wav');
Here's an example when used to prevent unnecessary execution of plug-in code; shouldn't be necessary if the wp_is_file_request() check is implemented during WordPress Bootstrap/Load, but just in case.:
<?php class RDP_PLUGIN { private static $_instance = NULL; private function __construct() { // prevent running code unnecessarily if(wp_is_file_request())return; // run the plugin add_action('wp_loaded', [$this, 'run'],1); }//__construct public static function get_instance(){ if (NULL === self::$_instance) self::$_instance = new self(); return self::$_instance; } //get_instance ... other plugin code ... } $RDP_PLUGIN = RDP_PLUGIN::get_instance();
Change History (5)
This ticket was mentioned in PR #8513 on WordPress/wordpress-develop by @rpayne7264.
5 weeks ago
#1
- Keywords has-patch added
#2
follow-ups:
↓ 3
↓ 4
@
4 weeks ago
Hi @rpayne7264
We are going to need some tests for this function.
Is this something you could tackle?
We will need to fix some whitespace issues with the patch as well.
Paul
#3
in reply to:
↑ 2
@
4 weeks ago
What tests?
What whitespace issues?
Replying to pbearne:
Hi @rpayne7264
We are going to need some tests for this function.
Is this something you could tackle?
We will need to fix some whitespace issues with the patch as well.
Paul
#4
in reply to:
↑ 2
@
4 weeks ago
@pbearne Looks like I got all of the whitespace issues fixed. Nothing is showing up in the PR compliance check regarding whitespace issues.
Replying to pbearne:
Hi @rpayne7264
We are going to need some tests for this function.
Is this something you could tackle?
We will need to fix some whitespace issues with the patch as well.
Paul
1) Added new function 'wp_is_file_request()' to wp-includes/functions.php
2) Added check at top of wp-includes/template-loader.php to prevent unnecessary processing when a missing file is being requested.
Trac ticket: https://core.trac.wordpress.org/ticket/63111