A must-use plugin loads before regular plugins and cannot be deactivated from the dashboard, which makes it a good home for small security rules. The snippet below is written defensively and does nothing risky.
What should a login-hardening snippet cover? #
At minimum it should stop username discovery through the REST API and author archives, disable XML-RPC if you do not use it, and record failed login attempts. These three changes remove the most common automated probing without needing another large plugin.
The snippet #
<?php
/**
* Plugin Name: Login Hardening (MU)
* Description: Small, defensive login protections.
*/
if ( ! defined( 'ABSPATH' ) ) { return; }
// Hide the user list from logged-out REST requests.
add_filter( 'rest_endpoints', function ( $endpoints ) {
if ( ! is_user_logged_in() ) {
foreach ( array_keys( $endpoints ) as $route ) {
if ( 0 === strpos( $route, '/wp/v2/users' ) ) { unset( $endpoints[ $route ] ); }
}
}
return $endpoints;
} );
// Disable XML-RPC.
add_filter( 'xmlrpc_enabled', '__return_false' );
// Log failed logins to debug.log.
add_action( 'wp_login_failed', function ( $username ) {
error_log( '[login-failed] ' . sanitize_user( $username ) );
} );
Install it #
- Create the folder
wp-content/mu-plugins/if it does not exist. - Save the snippet as
login-hardening.phpinside it. - Load the login page and confirm there are no PHP errors in
debug.log.
Roll back #
Delete the file. Because it is a single file with no database changes, removing it restores the previous behavior immediately.