For one of my projects, I needed to rename uploaded files so that their name contains the hash of their contents. Doing this makes the names CDN-friendly and avoids unnecessary purges. For example, consider the scenario when you upload a wrong file, delete it, and re-upload the correct file with the same name. If you click “Preview” after you have uploaded the wrong file, the CDN may cache it. Then, if you delete the file and re-upload a different one using the same name, chances are the CDN will still show you the old file. You then will need to go to the CDN and request a purge of the wrong file. And for some CDNs (hello, Photon!), this is even not possible.

For such scenarios, you need to make sure that the file name is always unique. One of the ways to ensure this is to add a hash of the file’s contents to its filename. Similar to how Webpack does this.

Luckily, there is an easy way to do this.

WordPress has a filter, wp_handle_upload_prefilter, to handle uploads (and a wp_handle_sideload_prefilter to handle sideloads). The only argument passed to the filter is the entry from the $_FILES array.

That array has three useful items:

  • error: the error code associated with this file upload or zero if everything is OK;
  • tmp_name: the temporary filename of the file in which the uploaded file was stored on the server;
  • name: the original name of the file on the client machine.

We can check if the error is zero to ensure that the file was uploaded successfully. The tmp_name refers to the uploaded file on the server, and we can use it to get the hash of the file’s contents. Finally, WordPress uses name to construct the final name of the uploaded file. If we modify the name’s value, this will affect the final name of the uploaded file.

Here is the sample code:

add_filter( 'wp_handle_upload_prefilter', function ( array $file ): array {
    if ( empty( $file['error'] ) && file_exists( $file['tmp_name'] ) ) {
        $hash = md5_file( $file['tmp_name'] );
        if ( false !== $hash ) {
            $info = pathinfo( $file['name'] );
            $ext  = empty( $info['extension'] ) ? '' : '.' . $info['extension'];
            $name = basename( $file['name'], $ext );

            $file['name'] = $name . '.' . substr( $hash, 0, 6 ) . $ext;
        }
    }

    return $file;
} );

If necessary, you can use a different hash algorithm by using the hash_file() function, and use a different length of the suffix by adjusting the last argument of substr(). Or even use a completely different naming scheme 🙂

How to Add a Content-Hash Suffix to Uploaded Files in WordPress
Tagged on:                 

Leave a Reply

Your email address will not be published. Required fields are marked *