Disable XML-RPC in WordPress Without a Plugin

Disabling XML-RPC in WordPress is a common hardening step. xmlrpc.php is a favourite target for password guessing and for pingback abuse, where bots make your site send requests to other servers.

The usual one-liner, add_filter( 'xmlrpc_enabled', '__return_false' ), does less than its name suggests. It only refuses methods that need a login; pingback.ping, system.multicall and the other public methods keep answering. This snippet turns all of them off and stops advertising the endpoint.

The snippet

<?php
// Refuse every XML-RPC method that needs a login.
add_filter( 'xmlrpc_enabled', '__return_false' );

// Remove all remaining methods, including pingback.ping and system.multicall.
add_filter( 'xmlrpc_methods', 'dpc_disable_xmlrpc_methods', PHP_INT_MAX );

function dpc_disable_xmlrpc_methods( $methods ) {
	return array();
}

// Stop advertising the endpoint in the X-Pingback response header.
add_filter( 'wp_headers', 'dpc_remove_x_pingback_header' );

function dpc_remove_x_pingback_header( $headers ) {
	unset( $headers['X-Pingback'] );

	return $headers;
}

Add it with Scripts Organizer

Scripts Organizer ships this snippet in its One-Click Import library. To add it by hand:

  • In wp-admin go to Scripts Organizer → Code Blocks → Add New and give the block a title.
  • Script location: PHP.
  • Trigger location: Everywhere. xmlrpc.php loads WordPress and its plugins like any other request, so Everywhere code is in place before the XML-RPC server reads its method list. Admin only would never run there.
  • Action hook: leave it empty. The code only registers filters and actions, so it can run while plugins load.
  • Paste the snippet into the PHP editor, enable the block and publish it.

The same code also works unchanged in a small plugin or an mu-plugin file in wp-content/mu-plugins/.

How it works

  • xmlrpc_enabled returning false makes every authenticated method, such as publishing posts or wp.getUsersBlogs, return an error before the username and password are checked.
  • xmlrpc_methods runs when WordPress builds the XML-RPC server. Returning an empty array removes every method, so any call gets a “method not found” fault. The priority PHP_INT_MAX makes it run after plugins that add methods of their own.
  • wp_headers filters the HTTP headers WordPress sends with front-end pages. Removing X-Pingback stops telling scanners where the endpoint lives.

Customise it

Jetpack and some remote publishing tools still use XML-RPC. To keep the endpoint for them and only close the parts bots abuse, drop the xmlrpc_enabled and dpc_disable_xmlrpc_methods lines and remove just these methods:

add_filter( 'xmlrpc_methods', 'dpc_remove_pingback_methods' );

function dpc_remove_pingback_methods( $methods ) {
	unset( $methods['pingback.ping'], $methods['pingback.extensions.getPingbacks'], $methods['system.multicall'] );

	return $methods;
}

Gotchas

  • xmlrpc.php still loads WordPress and answers, it just has nothing to offer. To stop those requests before PHP runs at all, block the file at the web server or CDN.
  • Classic themes may print <link rel="pingback"> themselves with bloginfo( 'pingback_url' ) in header.php. Remove that line in a child theme if you want no trace left.
  • Also untick Allow link notifications from other blogs (pingbacks and trackbacks) on new posts in Settings → Discussion.
  • For more hardening, see Lockdown Admin Backend to your IP and WordPress Security Updates Email Alert Without a Plugin.

In the Scripts Organizer library Import it with one click from Scripts Organizer.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You’ll also get DPlugins product updates. No spam, and you can unsubscribe anytime.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You'll also get DPlugins product updates. No spam, and you can unsubscribe anytime.