On WordPress Pingbacks

WordPress supports Pingbacks and these are enabled by default. Having noticed a missing pingback from one of my posts and having made sure that it wasn’t caught as spam or still pending, I decided to investigate a bit and try to resend it manually.

On WordPress Pingbacks

Armed with the Pingback specification, and the XML-RPC RFC, I was able to successfully have a request cURLed over.

curl "https://.../xmlrpc.php" --header "Content-Type: text/xml" --data "<?xmlversion="1.0"?><methodCall><methodName>pingback.ping</methodName><params><param><value><string>https://.../</string></value></param><param><value><string>https://.../</string></value></param></params></methodCall>"

<?xml version="1.0"?>
<methodCall>
   <methodName>pingback.ping</methodName>
   <params>
      <param>
        <value><string>https://.source./</string></value>
      </param>
      <param>
        <value><string>https://.target./</string></value>
      </param>
   </params>
</methodCall>

For which I got a nice response:

<?xml version="1.0"?>
<methodResponse>
  <params>
    <param>
      <value>
      <string>Pingback from https://.source./ to https://.target./ registered. Keep the web talking! :-)</string>
      </value>
    </param>
  </params>
</methodResponse>

Mission accomplished, got the Pingback thorough, great! But what if…?

Almost immediately, lots of thoughts bounce into my head, both good and evil. But I won’t be cURLing aimlessly around and trying interesting things out, but will go read the WordPress source on how it implements its Pingback server and client.

The Pingback client

Client-side Pingbacks are sent using the do_all_pings function. This function is triggered via the do_pings action. This hook is scheduled to be pulled inside the _publish_post_hook function, which is called once a post is (surprise-surprise) published.

Back to the do_all_pings function, a query is run selecting all “unpung” posts, deleting each and every ping flag as it goes (ah, so no matter what happens [Pingback server not responding] it gets Pingbacked once and only once.

The actual Pingback function is here.

Some things to note:

  • A Pingback request is aborted after only 3 seconds
  • A 60-second set_time_limit is set regardless of what the actual time limit is
  • There is no way to get any error messages or requeue a Pingback

The combination of these three factors have almost surely stopped my pingback from reaching its destination, due to a network jam or some other problems. Of course, they’re there for a reason, but still, come on.

add_ping is called if the response code from the Pingback server is true or its error is set to 48, meaning “Pingback already registered”.

Get all your WordPress outgoing Pingbacks via SQL: SELECT pinged FROM wp_posts WHERE `pinged` != '';. These are \n delimited and have to be split outside of MySQL.

Problem remains – once a Pingback fails, it’s over. It doesn’t get requeued, no second chance, is there? You can’t even call the do_all_pings since it takes into account only posts that have their _pingme key set. You can still pingback() a post directly, which is good to know.

A nice thing to have would be something like “Out of X URLs in this post Y Pingbacks have been successfully received” and a [Re-ping] button. Will probably code this later this week for myself. Unless there’s already something of the like or someone beats me to it…

The Pingback server

Receiving Pingbacks in WordPress is handled by the built-in WordPress XML-RPC Server. The method for pingbacks is pingback.ping and takes two arguments as per specification.

While you’re there check out the pingback.extensions.getPingbacks method.

curl "codeseekah.com/xmlrpc.php" --header "Content-type: text/xml" --data "<?xml version="1.0"?><methodCall><methodName>pingback.extensions.getPingbacks</methodName><params><param><string>https://codeseekah.com/2012/03/11/ack-grep-vs-grep/</string></param></params></methodCall>"
<?xml version="1.0"?>
<methodResponse>
  <params>
    <param>
      <value>
      <array><data>
  <value><string>https://ecojoy.se/2012/03/10/ack-grep-vs-grep/</string></value>
</data></array>
      </value>
    </param>
  </params>
</methodResponse>

Handy! And not even part of any specification, although it did sort of aim to be part of the Pingback 2.0 spec, which hasn’t been released (yet?). This method hustled itself into WordPress 7 years ago. A bit of trivia there.

Back on track.

ping.pingback checks whether the second parameter (pagelinkedto) contains its domain. After a series of attempts a post_id is identified.

Then checks are performed to make sure the post has Pingbacks enabled, and to make sure that a Pingback has not already been posted (this is done against the comments table). The pinging server is then given 1 second of idle time to publish before trying to retrieve the page, after which a wp_remote_fopen call is made.

The data that comes back is analyzed and made sure to have:

  1. A title! Yes, some people like to have no titles on their pages, Pingbacks from untitled posts (a rarity, but still) will be ignored.
  2. The URL has to be present on the page and in a link context, meaning inside the href attribute of an anchor tag.

Once all these tests are passed – we’re good to go with a nice “Pingback from %1$s to %2$s registered. Keep the web talking! :-)” response. Yeehaaaw!

Stay tuned!