Skip to content

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

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

Pings/Trackbacks: Disable pings in non-production environments#11476
arcangelini wants to merge 4 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.

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)

*/
function wp_disable_trackback_for_environment( $post_id, $trackback_url ) {
if ( wp_should_disable_pings_for_environment() ) {
trackback_response( 1, __( 'Sorry, trackbacks are closed for this item.' ) );
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.

Should this rather be something like

__( 'Trackbacks are disabled in non-production environments.' )

?

Otherwise anyone debugging a staging site won't be able to distinguish between "pings are legitimately closed on this post" and "pings are blocked by environment".

* notifications, as well as incoming pingbacks and trackbacks) are disabled
* for non-production environments ('local', 'development', 'staging').
*
* @since 6.9.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.

Suggested change
* @since 6.9.0
* @since 7.1.0

I'm guessing we're skipping 7.0 since it's about general release time?

* Returning false re-enables pings in non-production environments.
* Returning true disables pings even in production.
*
* @since 6.9.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.

Same as above. There are several below as well 👍🏻

/**
* @ticket 64837
*/
public function test_trackback_hook_registered_in_non_production() {
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.

Is this testing any env? If not maybe just test_trackback_hook_is_registered

Comment on lines +177 to +179
$this->assertSame( 10, has_action( 'do_all_pings', 'do_all_pingbacks' ) );
$this->assertSame( 10, has_action( 'do_all_pings', 'do_all_trackbacks' ) );
$this->assertSame( 10, has_action( 'do_all_pings', 'generic_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.

optional: add a message arg here so that any failing hooks can be identified more quickly, e.g.,

		$this->assertSame( 10, has_action( 'do_all_pingbacks should still be hooked' ) );

* @param int $post_id Post ID related to the trackback.
* @param string $trackback_url Trackback URL.
*/
function wp_disable_trackback_for_environment( $post_id, $trackback_url ) {
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.

Wondering if we could register the hook with no args since we're not using them here, e.g., add_action( 'pre_trackback_post', 'wp_disable_trackback_for_environment', 10, 0 );

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.

2 participants