diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php
index 3c0e6d8b9f777..229c9a75416af 100644
--- a/src/wp-includes/default-filters.php
+++ b/src/wp-includes/default-filters.php
@@ -704,7 +704,8 @@
add_action( 'rest_api_init', 'wp_oembed_register_route' );
add_filter( 'rest_pre_serve_request', '_oembed_rest_pre_serve_request', 10, 4 );
-add_action( 'wp_head', 'wp_oembed_add_discovery_links' );
+add_action( 'wp_head', 'wp_oembed_add_discovery_links', 4 ); // Printed after feed_links() and feed_links_extra().
+add_action( 'wp_head', 'wp_oembed_add_discovery_links' ); // Unhooked the first time that wp_oembed_add_discovery_links() runs for back-compat.
add_action( 'wp_head', 'wp_oembed_add_host_js' ); // Back-compat for sites disabling oEmbed host JS by removing action.
add_filter( 'embed_oembed_html', 'wp_maybe_enqueue_oembed_host_js' );
diff --git a/src/wp-includes/embed.php b/src/wp-includes/embed.php
index c143ffe4a455d..b7567024aa194 100644
--- a/src/wp-includes/embed.php
+++ b/src/wp-includes/embed.php
@@ -332,8 +332,19 @@ function wp_oembed_register_route() {
*
* @since 4.4.0
* @since 6.8.0 Output was adjusted to only embed if the post supports it.
+ * @since 6.9.0 Now runs first at `wp_head` priority 4, with a fallback to priority 10. This helps ensure the discovery links appear within the first 150KB.
*/
function wp_oembed_add_discovery_links() {
+ if ( doing_action( 'wp_head' ) ) {
+ // For back-compat, short-circuit if a plugin has removed the action at the original priority.
+ if ( ! has_action( 'wp_head', 'wp_oembed_add_discovery_links', 10 ) ) {
+ return;
+ }
+
+ // Prevent running again at the original priority.
+ remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );
+ }
+
$output = '';
if ( is_singular() && is_post_embeddable() ) {
diff --git a/tests/phpunit/tests/oembed/discovery.php b/tests/phpunit/tests/oembed/discovery.php
index 41efe1cc2a378..8f0cacaeb6004 100644
--- a/tests/phpunit/tests/oembed/discovery.php
+++ b/tests/phpunit/tests/oembed/discovery.php
@@ -49,6 +49,9 @@ public function test_add_oembed_discovery_links_to_post() {
$expected .= '' . "\n";
$this->assertSame( $expected, get_echo( 'wp_oembed_add_discovery_links' ) );
+
+ add_filter( 'oembed_discovery_links', '__return_empty_string' );
+ $this->assertSame( '', get_echo( 'wp_oembed_add_discovery_links' ), 'Expected filtering oembed_discovery_links to empty string to result in no wp_oembed_add_discovery_links() output.' );
}
public function test_add_oembed_discovery_links_to_page() {
@@ -100,4 +103,39 @@ public function test_wp_oembed_add_discovery_links_non_embeddable_post_type_outp
$this->assertFalse( get_oembed_response_data( $post, 100 ) );
}
+
+ /**
+ * @ticket 64178
+ * @covers ::wp_oembed_add_discovery_links
+ */
+ public function test_wp_oembed_add_discovery_links_back_compat() {
+ $action = 'wp_head';
+ $old_priority = 10;
+ $new_priority = 4;
+ $callback = 'wp_oembed_add_discovery_links';
+
+ $this->assertTrue( has_action( $action, $callback, $old_priority ), 'Expected wp_oembed_add_discovery_links() to be hooked at wp_head with old priority.' );
+ $this->assertTrue( has_action( $action, $callback, $new_priority ), 'Expected wp_oembed_add_discovery_links() to be hooked at wp_head with new priority.' );
+
+ // Remove all wp_head actions and re-add just the later old one.
+ remove_all_actions( $action );
+ add_action( $action, $callback, $old_priority );
+ add_action( $action, $callback, $new_priority );
+
+ $post_id = self::factory()->post->create();
+ $this->go_to( get_permalink( $post_id ) );
+ $this->assertQueryTrue( 'is_single', 'is_singular' );
+
+ $mock_action = new MockAction();
+ add_filter( 'oembed_discovery_links', array( $mock_action, 'filter' ) );
+
+ $wp_head_output = get_echo( 'wp_head' );
+ $this->assertSame( 1, $mock_action->get_call_count() );
+
+ $expected = '' . "\n";
+ $expected .= '' . "\n";
+
+ $this->assertSame( $expected, $wp_head_output, 'Expected wp_head output to be the same as the wp_oembed_add_discovery_links() output.' );
+ $this->assertSame( $expected, get_echo( $callback ), 'Expected wp_oembed_add_discovery_links() output to be the same as the wp_head output when called outside of wp_head.' );
+ }
}