By the SurfaceCheckr team. Every finding maps to a check we run in production - see our methodology.
Your WordPress advertises a relay that can attack other people
Most security findings are about someone getting into your site. This one is the reverse: it is about your site being pointed at someone else. A WordPress install with XML-RPC left on carries a method, pingback.ping, whose whole job is to make your server go fetch a URL that a stranger picked. That is a useful primitive for an attacker and a bad one to be handing out for free, because it means your server can be aimed at a victim you have never heard of, or at the private network sitting behind it.
What makes this easy to miss is that WordPress does not hide the endpoint. It announces it. When XML-RPC is on, WordPress adds an X-Pingback response header to your pages pointing straight at /xmlrpc.php, so an attacker scanning for relays does not have to guess whether yours is live. You told them in a header they got with the homepage.
The header that hands out the address
Load a WordPress page and look at the response headers, not the body. If XML-RPC is enabled, one of them is X-Pingback, and its value is the full URL of the pingback endpoint. That header exists so other blogs can find where to send a pingback when they link to you, which was the point back when pingbacks felt like a good idea. Today it mostly serves as a signpost for anyone cataloguing usable relays.
A plain GET of that endpoint answers with WordPress's fixed stub, XML-RPC server accepts POST requests only., returned as a 405. That stub is the tell that the handler is really there and answering, not a soft-404 catch-all returning 200 to everything. The scanner treats the finding as present only when both hold: the X-Pingback header points at xmlrpc.php, and the endpoint answers with that exact stub. It never sends the pingback.ping call itself, that would be exercising the relay rather than observing it.
What the relay actually does
The pingback.ping method takes a URL as its first argument and makes your server request it. Turn that around and two attacks fall out.
The first is reflected, distributed denial of service. An attacker collects a large set of pingback-enabled WordPress sites, sends each one a pingback.ping naming the same target, and every server obediently fetches that target at once. The victim sees a flood coming from thousands of ordinary blogs instead of from the attacker, which is exactly the point: it is hard to block and it does not trace back. Your site is one of the reflectors, and the abuse traffic leaves under your IP.
The second is blind server-side request forgery. Because the URL you fetch is attacker-chosen, it does not have to be a public site. It can be http://169.254.169.254/, the cloud metadata service that hands out instance credentials, or an internal address like 10.0.0.1 that is only reachable from inside your network. The attacker cannot always read the full response through the pingback machinery, but they can often tell from timing and error differences what exists and what does not, which is enough to map internal hosts and probe services that were never meant to face the internet. This particular SSRF sat in WordPress core for about six years before it was widely documented, so it is old, understood, and easy to reach.
There is a third, quieter abuse: system.multicall lets a caller batch many method invocations into one request. Pointed at the login methods, that turns brute-forcing into "try a few hundred passwords per HTTP request" instead of one at a time, which is why a login you thought was rate-limited can still be hammered through XML-RPC.
Turning it off without breaking your site
The clean fix depends on whether anything you run still needs XML-RPC. Most sites do not. A shrinking set of integrations, older Jetpack features, some mobile publishing flows, still lean on it.
# advertised and answering GET / -> X-Pingback: .../xmlrpc.php GET /xmlrpc.php -> 405 "accepts POST requests only" POST pingback.ping -> server fetches attacker's URL
If nothing needs it, block /xmlrpc.php at the web server or CDN so the request never reaches PHP at all, which also stops the system.multicall brute-force path. A reputable security plugin (Wordfence, iThemes/Solid Security) does the same thing through the admin if you would rather not touch config. If an integration genuinely needs XML-RPC for publishing but not for pingbacks, keep the endpoint and filter xmlrpc_methods to drop pingback.ping and pingback.extensions.getPingbacks; that closes the DDoS and SSRF vectors while leaving the publishing methods alone. Either way, WordPress stops advertising a relay pointed outward.
How this is checked from outside
Whether your site is broadcasting a live pingback relay is settled without touching the relay: the X-Pingback header arrives with the homepage a browser already loads, and one plain GET of /xmlrpc.php confirms the handler answers. That is all SurfaceCheckr reads, the same signals any scanner cataloguing reflectors would collect, and it stops there. It never sends the pingback.ping or system.multicall call that would cross from watching the endpoint into using it, so the check reports that the relay is enabled and advertised, not that it fired one. If you are already tidying the WordPress surface, the author list your REST API gives away is the natural next stop, and the version fingerprint bots match against known exploits is worth a look while you are in there.
Read next
- Your tech stack is public. Does that matter?What an attacker sees before they touch your site
- Your WordPress is handing out its usernames, and the login page is right where bots expect itWhat an attacker sees before they touch your site
- Your front-end is calling an API that hands back everyone's data, not just the logged-in user'sWhat an attacker sees before they touch your site
- Your assetlinks.json and apple-app-site-association are public by design. Here's what they tell people.What an attacker sees before they touch your site
Find it before someone else does.
Paste your domain. The grade and issue count are free, and you'll see in a couple of minutes exactly what's reachable from outside.
External, read-only scan. We only request public URLs - never log in, never send attack traffic.