diff --git src/wp-admin/includes/ajax-actions.php src/wp-admin/includes/ajax-actions.php
index b6bd757..561c37b 100644
--- src/wp-admin/includes/ajax-actions.php
+++ src/wp-admin/includes/ajax-actions.php
@@ -2397,6 +2397,9 @@ function wp_ajax_query_attachments() {
 	if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) )
 		$query['post_status'] .= ',private';
 
+	// Filter query clauses to include filenames.
+	add_filter( 'posts_clauses', '_filter_query_attachment_filenames' );
+
 	/**
 	 * Filters the arguments passed to WP_Query during an Ajax
 	 * call for querying attachments.
diff --git src/wp-admin/includes/post.php src/wp-admin/includes/post.php
index dfcb3ec..b8b7cd0 100644
--- src/wp-admin/includes/post.php
+++ src/wp-admin/includes/post.php
@@ -1144,10 +1144,38 @@ function wp_edit_attachments_query_vars( $q = false ) {
 		$q['post_parent'] = 0;
 	}
 
+	// Filter query clauses to include filenames.
+	add_filter( 'posts_clauses', '_filter_query_attachment_filenames' );
+
 	return $q;
 }
 
 /**
+ * Filter the SQL clauses of an attachment query to include file names.
+ *
+ * @since 4.7.0
+ * @access private
+ *
+ * @param array $clauses An array including WHERE, GROUP BY, JOIN, ORDER BY,
+ *                       DISTINCT, fields (SELECT), and LIMITS clauses.
+ * @return array The modified clauses.
+ */
+function _filter_query_attachment_filenames( $clauses ) {
+	global $wpdb;
+	remove_filter( 'posts_clauses', __FUNCTION__ );
+
+	$clauses[ 'join' ] = " INNER JOIN {$wpdb->postmeta} ON ( {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id )";
+	$clauses[ 'groupby' ] = "{$wpdb->posts}.ID";
+
+	$clauses[ 'where' ] = preg_replace(
+		"/\({$wpdb->posts}.post_content (NOT LIKE|LIKE) (\'[^']+\')\)/",
+		"$0 OR ( {$wpdb->postmeta}.meta_key = '_wp_attached_file' AND {$wpdb->postmeta}.meta_value $1 $2 )",
+		$clauses[ 'where' ] );
+
+	return $clauses;
+}
+
+/**
  * Executes a query for attachments. An array of WP_Query arguments
  * can be passed in, which will override the arguments set by this function.
  *
diff --git tests/phpunit/tests/query/search.php tests/phpunit/tests/query/search.php
index a228f91..89693d0 100644
--- tests/phpunit/tests/query/search.php
+++ tests/phpunit/tests/query/search.php
@@ -280,6 +280,110 @@ class Tests_Query_Search extends WP_UnitTestCase {
 		$this->assertSame( array( $p1, $p3, $p2 ), $q->posts );
 	}
 
+	/**
+	 * Unfiltered search queries for attachment post types should not inlcude file
+	 * names to ensure the postmeta JOINs don't happen on the front end.
+	 *
+	 * @ticket 22744
+	 */
+	public function test_exclude_file_names_in_attachment_search_by_default() {
+		$attachment = self::factory()->post->create( array(
+			'post_type'    => 'attachment',
+			'post_status'  => 'publish',
+			'post_title'   => 'bar foo',
+			'post_content' => 'foo bar',
+			'post_excerpt' => 'This post has foo',
+		) );
+
+		add_post_meta( $attachment, '_wp_attached_file', 'some-image2.png', true );
+
+		// Pass post_type an array value.
+		$q = new WP_Query( array(
+			's'           => 'image2',
+			'fields'      => 'ids',
+			'post_type'   => 'attachment',
+			'post_status' => 'inherit',
+		) );
+
+		$this->assertNotEquals( array( $attachment ), $q->posts );
+	}
+
+	/**
+	 * @ticket 22744
+	 */
+	public function test_include_file_names_in_attachment_search_as_string() {
+		$attachment = self::factory()->post->create( array(
+			'post_type'    => 'attachment',
+			'post_status'  => 'publish',
+			'post_title'   => 'bar foo',
+			'post_content' => 'foo bar',
+			'post_excerpt' => 'This post has foo',
+		) );
+
+		add_post_meta( $attachment, '_wp_attached_file', 'some-image1.png', true );
+		add_filter( 'posts_clauses', '_filter_query_attachment_filenames' );
+
+		// Pass post_type a string value.
+		$q = new WP_Query( array(
+			's'           => 'image1',
+			'fields'      => 'ids',
+			'post_type'   => 'attachment',
+			'post_status' => 'inherit',
+		) );
+
+		$this->assertSame( array( $attachment ), $q->posts );
+	}
+
+	/**
+	 * @ticket 22744
+	 */
+	public function test_include_file_names_in_attachment_search_as_array() {
+		$attachment = self::factory()->post->create( array(
+			'post_type'    => 'attachment',
+			'post_status'  => 'publish',
+			'post_title'   => 'bar foo',
+			'post_content' => 'foo bar',
+			'post_excerpt' => 'This post has foo',
+		) );
+
+		add_post_meta( $attachment, '_wp_attached_file', 'some-image2.png', true );
+		add_filter( 'posts_clauses', '_filter_query_attachment_filenames' );
+
+		// Pass post_type an array value.
+		$q = new WP_Query( array(
+			's'           => 'image2',
+			'fields'      => 'ids',
+			'post_type'   => array( 'attachment' ),
+			'post_status' => 'inherit',
+		) );
+
+		$this->assertSame( array( $attachment ), $q->posts );
+	}
+
+	/**
+	 * @ticket 22744
+	 */
+	public function test_exclude_attachment_file_names_in_general_searches() {
+		$attachment = self::factory()->post->create( array(
+			'post_type'    => 'attachment',
+			'post_status'  => 'publish',
+			'post_title'   => 'bar foo',
+			'post_content' => 'foo bar',
+			'post_excerpt' => 'This post has foo',
+		) );
+
+		add_post_meta( $attachment, '_wp_attached_file', 'some-image3.png', true );
+
+		$q = new WP_Query( array(
+			's'           => 'image3',
+			'fields'      => 'ids',
+			'post_type'   => array( 'post', 'page', 'attachment' ),
+			'post_status' => 'inherit',
+		) );
+
+		$this->assertNotEquals( array( $attachment ), $q->posts );
+	}
+
 	public function filter_posts_search( $sql ) {
 		return $sql . ' /* posts_search */';
 	}
