Basic WordPress Security
Here are a handful of basic things I try to do for any WordPress site for my own peace of mind.
Disable the XML-RPC API
The XML-RPC API is a legacy technology that predates the REST API. There's still some legitimate use for it, specifically some amount of the Jetpack suite uses the XML-RPC API. In previous eras misuse of the XML-RPC API was a notable security issue. You have multiple options for disabling this API. You can use a plugin like Disable XML-RPC-API. I usually drop a PHP snippet into the functions.php
.
add_filter( 'xmlrpc_enabled', '__return_false' );
add_action(
'xmlrpc_call',
function () {
wp_die( 'XML-RPC services are disabled on this site.', 'XML-RPC Disabled', array( 'response' => 403 ) );
}
);
The filter is the necessary part, the action is likely never going to be used but I like to explicitly set a diagnostic message if possible should someone be expecting the API to be open.
Occasionally people recommend renaming or moving the endpoint/slug. Do not do that. Security by obscurity was maybe a legitimate technique in the 90's, in the era of LLM-driven bots techniques like this are a waste of time at best.
Restrict the user route of the REST API
This is, of course, only advisable if you're not using this piece of the API. It's pretty common to try to iterate over the user route of the WordPress REST API as a precursor to a user enumeration attack. Save yourself the bandwidth and just block the whole route. You can do that via a PHP snippet in your functions.php
.
add_filter(
'rest_authentication_errors',
function ( $access ) {
if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
return $access;
}
if ( ! is_user_logged_in() || ! current_user_can( 'list_users' ) ) {
$requested_route = filter_var( $_SERVER['REQUEST_URI'], FILTER_SANITIZE_URL );
if ( false !== $requested_route && false !== strpos( $requested_route, '/wp/v2/users' ) ) {
return new WP_Error( 'rest_forbidden', 'Sorry, you are not allowed to do that.', array( 'status' => 403 ) );
}
}
return $access;
}
);
Disable comments
Generally speaking I do not like accepting user-generated content of any type from the public and saving it to a site's database directly. I always remove the native ability in WordPress to leave comments. (Go with a third-party if you must.) In order to be effective you need to do more than just remove the forms from your templates. That does not disable comments, only prevents people from submitting comments easily. Bots can still easily submit comments. You could disable comments via a series of PHP snippets, I just prefer to use a plugin like Disable Comments.
Serve the correct security headers
This is very dependent on the environment your site is hosted in, so there's not necessarily a one-size-fits-all solution. Worst-case scenario you can use the HTTP Headers plugin or use a PHP snippet that vaguely takes this form:
add_action(
'send_headers',
function () {
header( 'X-Content-Type-Options: nosniff' );
header( 'X-Frame-Options: SAMEORIGIN' );
header( 'Permissions-Policy: geolocation=(), microphone=(), camera=()' );
// These are examples. You'll need to do some research to determine what you really need to use.
}
);