Make WordPress Core

Opened 5 weeks ago

Last modified 4 weeks ago

#63111 new enhancement

short-circuit WordPress processing for requests of missing files

Reported by: rpayne7264's profile rpayne7264 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

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

#2 follow-ups: @pbearne
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 @rpayne7264
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 @rpayne7264
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

#5 @pbearne
4 weeks ago

Hi @rpayne7264

Thank you

How do you feel about creating the PHPunit tests?

Note: See TracTickets for help on using tickets.