Skip to content

Pings/Trackbacks: Disable pings in non-production environments#11476

Open
arcangelini wants to merge 6 commits intoWordPress:trunkfrom
arcangelini:disable-pings-non-production
Open

Pings/Trackbacks: Disable pings in non-production environments#11476
arcangelini wants to merge 6 commits intoWordPress:trunkfrom
arcangelini:disable-pings-non-production

Conversation

@arcangelini
Copy link
Copy Markdown

@arcangelini arcangelini commented Apr 8, 2026

Trac Ticket

https://core.trac.wordpress.org/ticket/64837

Summary

When WP_ENVIRONMENT_TYPE is local, development, or staging, pingbacks and trackbacks are sent when posts are published. This creates confusion on the receiving end and is unnecessary for testing workflows.

This PR disables all outgoing and incoming pings in non-production environments by default:

  • Outgoing: Removes do_all_pingbacks, do_all_trackbacks, and generic_ping callbacks from the do_all_pings action (at priority 1, before they fire at priority 10). Enclosures (do_all_enclosures) are intentionally left untouched.
  • Incoming pingbacks: Filters xmlrpc_methods to remove pingback.ping.
  • Incoming trackbacks: Hooks pre_trackback_post to reject trackbacks before they are processed.

Note: pings_open() is intentionally not filtered, because it would suppress the X-Pingback header that wp_install_maybe_enable_pretty_permalinks() relies on to verify rewrite rules during fresh installs.

Override filter

Developers can re-enable pings in non-production environments:

add_filter( 'wp_should_disable_pings_for_environment', '__return_false' );

Or disable pings even in production:

add_filter( 'wp_should_disable_pings_for_environment', '__return_true' );

Test instructions

  1. Set WP_ENVIRONMENT_TYPE to local, development, or staging
  2. Publish a post with links to external sites
  3. Verify no outgoing pingbacks or trackbacks are sent
  4. Verify incoming pingback XML-RPC calls are rejected
  5. Verify incoming trackbacks are rejected
  6. Change WP_ENVIRONMENT_TYPE to production and verify pings work normally
  7. Run npm run test:php -- --filter Tests_Comment_DisablePingsForEnvironment

Files changed

  • src/wp-includes/comment.php — Four new functions: wp_should_disable_pings_for_environment() (public API with filter), wp_disable_outgoing_pings_for_environment(), wp_disable_trackback_for_environment(), wp_disable_xmlrpc_pingback_for_environment()
  • src/wp-includes/default-filters.php — Register the three hook callbacks
  • tests/phpunit/tests/comment/disablePingsForEnvironment.php — 17 new tests covering all environment types, filter overrides, and edge cases

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@arcangelini arcangelini force-pushed the disable-pings-non-production branch from 16bf231 to efa3dfb Compare April 8, 2026 18:05
The global pings_open filter suppressed the X-Pingback header which
wp_install_maybe_enable_pretty_permalinks() uses to verify rewrite
rules, breaking pretty permalinks on fresh non-production installs.

Use pre_trackback_post action to reject incoming trackbacks instead,
which only affects the wp-trackback.php entry point. Reverts the
pingsOpen and sendHeaders test changes since pings_open is no longer
filtered.
@arcangelini arcangelini closed this Apr 8, 2026
@arcangelini arcangelini reopened this Apr 8, 2026
@arcangelini arcangelini marked this pull request as ready for review April 8, 2026 19:04
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props arcangelini, ramonopoly, tyxla.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Copy link
Copy Markdown
Member

@ramonjd ramonjd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

Things are working as expected in my testing. It'd be good to get a second eye on this to confirm.

cc @cagrimmett

I tested by switching my config from define( 'WP_ENVIRONMENT_TYPE', 'local' ); to define( 'WP_ENVIRONMENT_TYPE', 'production' ); and back again.

Verified the method was returning what we expect:

wp eval "var_dump( wp_should_disable_pings_for_environment() );" // => bool(true)
wp eval "define('WP_ENVIRONMENT_TYPE','production'); var_dump( wp_should_disable_pings_for_environment() );" // =>  bool(false)

Verify outgoing pings are suppressed

wp eval "do_action('do_all_pings'); var_dump( has_action('do_all_pings','do_all_pingbacks') );"

production: int(10)
local: bool(false)

Verify incoming XML-RPC pingback is removed

wp eval "
\$result = wp_disable_xmlrpc_pingback_for_environment( array(
    'pingback.ping' => 'test',
    'wp.getPost'    => 'test',
) );
var_dump( \$result );

production:

array(2) {
  ["pingback.ping"]=>
  string(4) "test"
  ["wp.getPost"]=>
  string(4) "test"
}

local:

array(1) {
  ["wp.getPost"]=>
  string(4) "test"
}

And with the filter

wp eval "
\$methods = apply_filters( 'xmlrpc_methods', array(
    'pingback.ping' => 'test',
    'wp.getPost'    => 'test',
) );
var_dump( array_keys( \$methods ) );" 

production:

array(2) {
  [0]=>
  string(13) "pingback.ping"
  [1]=>
  string(10) "wp.getPost"
}

local:

array(1) {
  [0]=>
  string(10) "wp.getPost"
}

Override filter is working

wp eval "add_filter('wp_should_disable_pings_for_environment','__return_false');
var_dump( wp_should_disable_pings_for_environment() );" // => bool(false)

Copy link
Copy Markdown
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be working well in my testing. 👍

I do have some precision and accuracy suggestions, as well as some edge case handling to be considered.

add_action( 'do_all_pings', 'generic_ping', 10, 0 );

// Disable pings (pingbacks, trackbacks, and ping service notifications) in non-production environments.
add_action( 'do_all_pings', 'wp_disable_outgoing_pings_for_environment', 1, 0 );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth checking if there are any major plugins tinkering with this filter, and re-adding these callbacks before/at the default priority of 10. We can use https://wpdirectory.net/ to be sure.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth checking if there are any major plugins tinkering with this filter

Ran the wpdirectory search for ['"]do_all_pings['"] and went through all 22 hits. Most are references (JSON exclude lists, hook dumps, doc snippets) while the rest are removing this action from the do_pings hook. Seems like we are safe?

*/
function wp_disable_xmlrpc_pingback_for_environment( $methods ) {
if ( wp_should_disable_pings_for_environment() ) {
unset( $methods['pingback.ping'] );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we intentionally leaving the readonly pingback.extensions.getPingbacks() intact for non-production environments?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly it was a bit of an oversight initially, but thinking about it more I'm leaning toward leaving it alone. It's a read-only SELECT against the comments table, so it feels like it sits outside what this PR is really targeting (staging pinging prod, or accepting incoming pings).

The other thing that stopped me was that existing pingbacks are still visible in the admin, REST API, get_comments(), etc. on non-prod. Stripping just this one read method while leaving all those alone felt inconsistent. I am open on this though.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, maybe we should leave it alone. Maybe the site owner disabled pingbacks after having them enabled for a while before.

@arcangelini arcangelini requested a review from ramonjd April 10, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants