How to Use UUID for WordPress Author URL

In WordPress, you can register multiple authors, and each author will have their own URL. The problem is that this author URL shows the author’s username, which poses a security risk for your WordPress site. Since the author username is exposed, attackers could use it to attempt to log in or brute-force their way into your site.

To solve this problem, we can mask the author URL with a randomized ID like UUID. This way, the author URL will not reveal the author’s username and will be more secure.

WordPress security illustration showing a shield protecting user profiles

We’ll be looking at two approaches: The hard way, where we write the code ourselves, and the easy way, where we use a plugin.

So, without further ado, let’s see how it works.

The Hard Way

To begin, create a new PHP file, for example uuid-slug.php, inside either the /wp-content/plugin directory or the /wp-content/mu-plugins/ directory, to load it as a must-use plugin. This file will contain the plugin headers…

/**
 * Plugin bootstrap file.
 *
 * This file is read by WordPress to display the plugin's information in the admin area.
 *
 * @wordpress-plugin
 * Plugin Name:       Author UUID Slug
 * Plugin URI:        https://github.com/hongkiat/wp-author-uuid-slug
 * Description:       Use UUID for the author URL.
 * Version:           1.0.0
 * Requires at least: 6.0
 * Requires PHP:      7.4
 * Author:            Thoriq Firdaus
 * Author URI:        https://github.com/tfirdaus
 */

…and the logic required to implement UUID-based author URLs. In this case, we will provide a simple input in the user profile editor to add the UUID.

add_action('show_user_profile', 'add_uuid_field_to_profile');
add_action('edit_user_profile', 'add_uuid_field_to_profile');

function add_uuid_field_to_profile($user)
{
    $uuid = get_user_meta($user->ID, '_uuid', true);
    ?>
    <table class="form-table">
        <tr>
            <th><label for="user_uuid"><?php esc_html_e('UUID', 'hongkiat'); ?></label></th>
            <td>
                <input 
                    type="text" 
                    name="user_uuid" 
                    id="user_uuid" 
                    value="<?php echo esc_attr($uuid); ?>" 
                    class="regular-text" 
                    <?php echo !current_user_can('manage_options') ? 'readonly' : ''; ?>
                />
                <p class="description">
                    <?php 
                        if (current_user_can('manage_options')) {
                            esc_html_e('Enter or update the UUID for this user.', 'hongkiat');
                        } else {
                            esc_html_e('This UUID is read-only for non-administrators.', 'hongkiat');
                        }
                    ?>
                </p>
            </td>
        </tr>
    </table>
    <?php
}

add_action('personal_options_update', 'save_uuid_field');
add_action('edit_user_profile_update', 'save_uuid_field');

function save_uuid_field($user_id)
{
    if (!current_user_can('manage_options', $user_id)) {
        return false;
    }

    $new_uuid = isset($_POST['user_uuid']) ? sanitize_text_field($_POST['user_uuid']) : '';

    if (!empty($new_uuid) && is_uuid($new_uuid)) {
        update_user_meta($user_id, '_uuid', $new_uuid);
    } else {
        delete_user_meta($user_id, '_uuid');
    }
}

function is_uuid($value)
{
    $pattern = '/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i'; // UUID pattern.

    return (bool) preg_match($pattern, $value);
}

For security reasons, this input will only be active and editable for users with the manage_options permission, so only administrators will be able to add or update the UUID for users. Users without the proper permissions will see the input as read-only.

Change the Author URL

Next, we need to modify the author URL to use the UUID instead of the author’s username. This can be achieved by implementing the author_link filter, as shown below:

add_filter('author_link', 'change_author_url', 10, 3);

function change_author_url($link, $author_id, $author_nicename)
{
    $uuid = get_user_meta($author_id, '_uuid', true);

    if (is_string($uuid)) {
        return str_replace('/' . $authorSlug, '/' . $uuid, $link);
    }

    return $link;
}

This implementation will update the generated URL for the author, affecting both the front-end theme and the admin interface.

WordPress admin panel showing author URL with UUID implementation
Handling Queries for Author Archives

Since we’ve modified the URL structure for author archive URLs, we also need to handle the corresponding queries. Without this, WordPress would return a 404 Not Found error because it wouldn’t recognize how to query authors by their _uuid metadata.

To implement this functionality, we can utilize the pre_get_posts hook as shown below:

add_action('pre_get_posts', 'author_uuid_query');

function author_uuid_query($query) {
    /**
     * If the permalink structure is set to plain, the author should be queried
     * by the user ID.
     */
    if ((bool) get_option('permalink_structure') === false) {
        return;
    }

    $author_name = $query->query_vars['author_name'] ?? '';

    if (! is_string($author_name) || ! is_uuid($author_name)) {
        $query->is_404 = true;
        $query->is_author = false;
        $query->is_archive = false;

        return;
    }

    $users = get_users([
        'meta_key' => '_uuid',
        'meta_value' => $author_name,
    ]);

    if (count($users) <= 0) {
        $query->is_404 = true;
        $query->is_author = false;
        $query->is_archive = false;

        return;
    }

    $user = $users[0];

    if (! $user instanceof WP_User) {
        $query->is_404 = true;
        $query->is_author = false;
        $query->is_archive = false;

        return;
    }

    $query->set('author_name', $user->user_nicename);
}

The code above verifies whether the permalink structure is set to something other than the default “Plain” setting. We exclude handling queries for the “Plain” permalink structure because WordPress uses the author ID (?author=<id>) rather than the author_name in this case.

Changing the Author Slug in REST API

The user’s username is also exposed in the /wp-json/wp/v2/users REST API endpoint. To enhance security, we’ll modify this by replacing the username with the UUID. This can be accomplished by implementing the rest_prepare_user hook as demonstrated below:

add_filter('rest_prepare_user', 'change_user_slug_in_rest_api', 10, 2);

function change_user_slug_in_rest_api($response, $user)
{
    $data = $response->get_data();

    if (is_array($data)) {
        $uuid = get_user_meta($author_id, '_uuid', true);

        if (is_string($uuid)) {
            $data['slug'] = $uuid;
        }
    }

    $response->set_data($data);

    return $response;
}

With this implementation, the author URL will now utilize the UUID instead of the username. Any attempts to access the author URL using the original username will result in a 404 not found error.

While this solution works effectively for smaller sites or those with limited users, it can become cumbersome to manage when dealing with a large number of users. In such cases, implementing UUIDs manually for each user would be time-consuming and impractical.

Therefore, let’s explore an alternative approach that offers a more streamlined solution.

The Easy Way

For a simpler solution, we’ll utilize a plugin called Feature Flipper. This plugin provides several security features, including the ability to obfuscate usernames using UUIDs.

You can install the plugin directly from the Plugins section in your WordPress dashboard. After installation and activation, navigate to Settings > Feature > Security and enable the Obfuscate Usernames option.

WordPress Feature Flipper plugin settings interface showing security options

Once you’ve saved the settings, the plugin will automatically generate UUIDs for all existing users on your site. Additionally, it will assign UUIDs to any new users upon registration.

Conclusion

Implementing UUIDs for author URLs is an effective security measure that helps protect your WordPress site by concealing author usernames. This approach significantly reduces the risk of brute-force attacks and unauthorized access attempts.

Throughout this tutorial, we’ve explored two implementation methods. For those who prefer a custom solution, the complete source code is available in our GitHub repository. Alternatively, the Feature Flipper plugin offers a more straightforward approach for users seeking a ready-made solution.

WebsiteFacebookTwitterInstagramPinterestLinkedInGoogle+YoutubeRedditDribbbleBehanceGithubCodePenWhatsappEmail