Limit default Search query to post_title

After searching through two dozen blog posts I was able to combined several approaches and make this work.

function my_search_by_title_only( $search, $wp_query ) {
  if ( ! empty( $search ) && ! empty( $wp_query->query_vars['search_terms'] ) ) {
    global $wpdb;
    $q = $wp_query->query_vars;
    $n = ! empty( $q['exact'] ) ? '' : '%';
    $search = array();
    foreach ( ( array ) $q['search_terms'] as $term )
    $search[] = $wpdb->prepare( "$wpdb->posts.post_title LIKE %s", $n . $wpdb->esc_like( $term ) . $n );
    if ( ! is_user_logged_in() )
    $search[] = "$wpdb->posts.post_password = ''";
    $search=" AND " . implode( ' AND ', $search );
  }
  return $search;
}
add_filter( 'posts_search', 'my_search_by_title_only', 10, 2 );

Unlike other, older solutions, this will not adversely affect things like menus or the latest blog posts page.

This will match the search term in post titles only, massively speeding searches on sites with large numbers of posts. This works on both the front end and admin so it’s also useful when ACF Relationship Field lookups are really slow.