Index: src/wp-admin/includes/ajax-actions.php
===================================================================
--- src/wp-admin/includes/ajax-actions.php	(revision 41967)
+++ src/wp-admin/includes/ajax-actions.php	(working copy)
@@ -3034,6 +3034,15 @@
 	$parsed = false;
 	$wp_embed->return_false_on_fail = true;
 
+	if ( 0 === $post_id ) {
+		/*
+		 * Refresh oEmbeds cached outside of posts that are past their TTL.
+		 * Posts are excluded because they have separate logic for refreshing
+		 * their post meta caches. See WP_Embed::cache_oembed().
+		 */
+		$wp_embed->usecache = false;
+	}
+
 	if ( is_ssl() && 0 === strpos( $url, 'http://' ) ) {
 		// Admin is ssl and the user pasted non-ssl URL.
 		// Check if the provider supports ssl embeds and use that for the preview.
Index: src/wp-includes/class-wp-embed.php
===================================================================
--- src/wp-includes/class-wp-embed.php	(revision 41967)
+++ src/wp-includes/class-wp-embed.php	(working copy)
@@ -284,13 +284,35 @@
 				kses_remove_filters();
 			}
 
-			wp_insert_post( wp_slash( array(
-				'post_name'    => $key_suffix,
-				'post_content' => $html ? $html : '{{unknown}}',
-				'post_status'  => 'publish',
-				'post_type'    => 'oembed_cache',
-			) ) );
+			$insert_post_args = array(
+				'post_name' => $key_suffix,
+				'post_status' => 'publish',
+				'post_type' => 'oembed_cache',
+			);
 
+			if ( $html ) {
+				if ( $cached_post_id ) {
+					wp_update_post( wp_slash( array(
+						'ID' => $cached_post_id,
+						'post_content' => $html,
+					) ) );
+				} else {
+					wp_insert_post( wp_slash( array_merge(
+						$insert_post_args,
+						array(
+							'post_content' => $html,
+						)
+					) ) );
+				}
+			} elseif ( ! $cache ) {
+				wp_insert_post( wp_slash( array_merge(
+					$insert_post_args,
+					array(
+						'post_content' => '{{unknown}}',
+					)
+				) ) );
+			}
+
 			if ( $has_kses ) {
 				kses_init_filters();
 			}
Index: tests/phpunit/tests/oembed/WpEmbed.php
===================================================================
--- tests/phpunit/tests/oembed/WpEmbed.php	(revision 41967)
+++ tests/phpunit/tests/oembed/WpEmbed.php	(working copy)
@@ -272,6 +272,35 @@
 		$this->assertEquals( '{{unknown}}', $post_content );
 	}
 
+	public function test_shortcode_should_update_custom_post() {
+		add_filter( 'oembed_ttl', '__return_zero' );
+
+		$url        = 'https://example.com/';
+		$embedded   = '<b>Embedded content</b>';
+		$key_suffix = md5( $url . serialize( wp_embed_defaults( $url ) ) );
+
+		add_filter( 'pre_oembed_result', '__return_empty_string' );
+		$this->wp_embed->shortcode( array(), $url );
+		remove_filter( 'pre_oembed_result', '__return_empty_string' );
+
+		$oembed_post_id = $this->wp_embed->find_oembed_post_id( $key_suffix );
+
+		$this->assertSame( '{{unknown}}', get_post( $oembed_post_id )->post_content );
+
+		$previous_usecache = $this->wp_embed->usecache;
+		$this->wp_embed->usecache = false;
+
+		// The update cannot be empty because empty responses won't overwrite the cache.
+		add_filter( 'pre_oembed_result', array( $this, '_pre_oembed_result_callback' ) );
+		$this->wp_embed->shortcode( array(), $url );
+		remove_filter( 'pre_oembed_result', array( $this, '_pre_oembed_result_callback' ) );
+
+		$this->assertSame( $embedded, get_post( $oembed_post_id )->post_content );
+
+		$this->wp_embed->usecache = $previous_usecache;
+		remove_filter( 'oembed_ttl', '__return_zero' );
+	}
+
 	public function test_shortcode_should_get_url_from_src_attribute() {
 		$url    = 'http://example.com/embed/foo';
 		$actual = $this->wp_embed->shortcode( array( 'src' => $url ) );
