By default the WordPress search looks for the searched words in the post title, the excerpt and the content. On a site with long articles, a search for a product or a person returns every post that mentions it once, and the post that is actually about it gets lost in the list.
This snippet limits the WordPress search to the post title only. It changes the main search on the front end and leaves wp-admin, the REST API and custom queries alone.
The snippet
<?php
/**
* Make the WordPress front-end search match post titles only, not the content.
*/
add_filter( 'posts_search', 'dpc_search_by_title_only', 10, 2 );
function dpc_search_by_title_only( $search, $wp_query ) {
if ( '' === $search || empty( $wp_query->query_vars['search_terms'] ) || ! dpc_search_by_title_applies( $wp_query ) ) {
return $search;
}
global $wpdb;
$q = $wp_query->query_vars;
$n = ! empty( $q['exact'] ) ? '' : '%';
$prefix = (string) apply_filters( 'wp_query_search_exclusion_prefix', '-' );
$where = array();
foreach ( (array) $q['search_terms'] as $term ) {
// "-word" excludes titles with that word, like the default search does.
$exclude = '' !== $prefix && str_starts_with( $term, $prefix );
if ( $exclude ) {
$term = substr( $term, strlen( $prefix ) );
}
if ( '' === $term ) {
continue;
}
$where[] = $wpdb->prepare(
"{$wpdb->posts}.post_title " . ( $exclude ? 'NOT LIKE' : 'LIKE' ) . ' %s',
$n . $wpdb->esc_like( $term ) . $n
);
}
if ( ! $where ) {
return $search;
}
$search = ' AND (' . implode( ' AND ', $where ) . ')';
if ( ! is_user_logged_in() ) {
$search .= " AND ({$wpdb->posts}.post_password = '')";
}
return $search;
}
/** Only the main search on the front end; wp-admin lists, REST and custom queries keep the default search. */
function dpc_search_by_title_applies( $wp_query ) {
$applies = ! is_admin() && $wp_query->is_main_query() && $wp_query->is_search();
return (bool) apply_filters( 'dpc_search_by_title_applies', $applies, $wp_query );
}
Add it with Scripts Organizer
In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Everywhere, tick PHP under Script location, paste the code into the PHP editor and publish. Leave Action/Hook empty. The filter runs when WordPress builds the search query, long after the code block has loaded.
The same code also works unchanged in a small plugin or an mu-plugin.
How it works
posts_searchreceives the SQLANDpart WordPress built for the search, plus the query. The snippet replaces it with onepost_title LIKEcondition per search word, so every word must appear in the title.- Each word goes through
$wpdb->esc_like()and$wpdb->prepare(), so%,_and quotes in a search cannot change the SQL. - A word starting with
-excludes titles containing it (zebra -night), as in the default search, and exact searches (exact=1) match the whole title. - Visitors do not find password-protected posts, as in the default search.
dpc_search_by_title_applies()limits it to the main query on the front end. The earlier version also changed searches in wp-admin post lists and in every customWP_Query.
Customise it
- Also in wp-admin lists: add
add_filter( 'dpc_search_by_title_applies', function ( $applies, $q ) { return $q->is_search(); }, 10, 2 );. - Only for one post type: return
$applies && 'product' === $q->get( 'post_type' )from the same filter. - Search only posts, not pages: see Limit the Search result to only POSTS.
Gotchas
- Search plugins such as Relevanssi or SearchWP replace the query themselves, so this filter may have no effect with them.
- A
LIKE '%word%'match also finds the word inside longer words:artmatchesParty. - WooCommerce product search on the shop page uses the same main query, so it also searches product titles only.
In the Scripts Organizer library Import it with one click from Scripts Organizer.