diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php
index 00782b1f33..15fef0d851 100644
--- a/src/wp-includes/formatting.php
+++ b/src/wp-includes/formatting.php
@@ -492,7 +492,8 @@ function wpautop( $pee, $br = true ) {
 	// Change multiple <br>s into two line breaks, which will turn into paragraphs.
 	$pee = preg_replace( '|<br\s*/?>\s*<br\s*/?>|', "\n\n", $pee );
 
-	$allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)';
+	$allblocks        = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)\b';
+	$allblocksexceptp = str_replace( '|p|', '|', $allblocks );
 
 	// Add a double line break above block-level opening tags.
 	$pee = preg_replace( '!(<' . $allblocks . '[\s/>])!', "\n\n$1", $pee );
@@ -558,12 +559,12 @@ function wpautop( $pee, $br = true ) {
 	// Under certain strange conditions it could create a P of entirely whitespace.
 	$pee = preg_replace( '|<p>\s*</p>|', '', $pee );
 
-	// Add a closing <p> inside <div>, <address>, or <form> tag if missing.
-	$pee = preg_replace( '!<p>([^<]+)</(div|address|form)>!', '<p>$1</p></$2>', $pee );
-
 	// If an opening or closing block element tag is wrapped in a <p>, unwrap it.
 	$pee = preg_replace( '!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', '$1', $pee );
 
+	// Add a closing <p> inside <div>, <address>, or <form> tag if missing.
+	$pee = preg_replace( '!<p>([^<]+)</(div|address|form)>!', '<p>$1</p></$2>', $pee );
+
 	// In some cases <li> may get wrapped in <p>, fix them.
 	$pee = preg_replace( '|<p>(<li.+?)</p>|', '$1', $pee );
 
@@ -577,6 +578,12 @@ function wpautop( $pee, $br = true ) {
 	// If an opening or closing block element tag is followed by a closing <p> tag, remove it.
 	$pee = preg_replace( '!(</?' . $allblocks . '[^>]*>)\s*</p>!', '$1', $pee );
 
+	// If a closing <p> tag is inside a block element tag, without a preceding opening <p> tag, remove it.
+	$pee = preg_replace( '#(<(' . $allblocksexceptp . ')[^>]*>)(((?!<p>|</\2>).)*)</p>#s', '$1$3', $pee );
+
+	// If an opening <p> tag is inside a block element tag, without a following closing <p> tag, remove it.
+	$pee = preg_replace( '#<p>(((?!</p>).)*</' . $allblocksexceptp . '>)#s', '$1', $pee );
+
 	// Optionally insert line breaks.
 	if ( $br ) {
 		// Replace newlines that shouldn't be touched with a placeholder.
diff --git a/tests/phpunit/tests/formatting/Autop.php b/tests/phpunit/tests/formatting/Autop.php
index e93e826695..f7b65edf51 100644
--- a/tests/phpunit/tests/formatting/Autop.php
+++ b/tests/phpunit/tests/formatting/Autop.php
@@ -600,4 +600,63 @@ line 2<br/>
 
 		$this->assertEquals( $expected, trim( wpautop( $content ) ) );
 	}
+
+	/**
+	 * wpautop() should remove stray <p> and </p> tags inside blocks
+	 *
+	 * @ticket 40135
+	 * @dataProvider data_wpautop_removes_stray_p_tags_in_blocks
+	 */
+	function test_wpautop_removes_stray_p_tags_in_blocks( $data, $expected ) {
+		$this->assertEquals( $expected, wpautop( $data ) );
+	}
+
+	function data_wpautop_removes_stray_p_tags_in_blocks() {
+		return array(
+			array(
+				'<div><p>123</p> </div>',
+				"<div>\n<p>123</p>\n</div>\n",
+			),
+			array(
+				'<div><p>123</p></div>',
+				"<div>\n<p>123</p>\n</div>\n",
+			),
+			array(
+				'hello<div>test</div>',
+				"<p>hello</p>\n<div>test</div>\n",
+			),
+			array(
+				'<div><p>Hello world</p><span>WordPress</span></div>',
+				"<div>\n<p>Hello world</p>\n<span>WordPress</span></div>\n",
+			),
+			array(
+				"<div>hello\n<pre>test</pre>\nworld</div>",
+				"<div>hello\n<pre>test</pre>\n<p>world</p></div>\n",
+			),
+			array(
+				'hello<div>test</div>',
+				"<p>hello</p>\n<div>test</div>\n",
+			),
+			array(
+				'<div><img src="/wp-content/uploads/example.jpg" alt="something" /><div>Something</div></div>',
+				"<div><img src=\"/wp-content/uploads/example.jpg\" alt=\"something\" />\n<div>Something</div>\n</div>\n",
+			),
+			array(
+				'<div><span></span><div></div></div>',
+				"<div><span></span>\n<div></div>\n</div>\n",
+			),
+			array(
+				'<div>X<div></div></div>',
+				"<div>X\n<div></div>\n</div>\n",
+			),
+			array(
+				"<div><div></div>\n </div>",
+				"<div>\n<div></div>\n</div>\n",
+			),
+			array(
+				"[banner]\n<h1>Test</h1>",
+				"<p>[banner]</p>\n<h1>Test</h1>\n",
+			),
+		);
+	}
 }
