搜索全站
建站 15 分钟阅读

WordPress站内搜索优化:搜索框、无结果页与相关性排序实战

站内搜什么都搜不到,用户转身就走。本文讲清 WordPress 默认搜索的排序缺陷,给出搜索框位置、无结果页兜底、按标题加权排序的代码方案,以及什么时候该上 Relevanssi 或 SearchWP。

U
UFO_PF 2026-03-09 · 阅读 15 分钟
Wo 建站
阅读提示
本文约 5,670 字,预计阅读 15 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:搜”退货”,出来的是三年前的新闻稿

内容做到一定量之后,站内搜索会成为高频入口。但 WordPress 默认的搜索体验相当糟糕:它只匹配 post_title 和 post_content,按发布时间倒序排,而且是把关键词拆成多个词做 AND 匹配——用户搜”退货流程”,只要有文章没同时包含”退货”和”流程”就搜不到,搜出来的结果里最新的排在最前,哪怕那篇只是顺带提了一句。

更糟的是无结果页。默认主题在搜不到时输出一句”抱歉,没有找到相关内容”,然后就是一个空荡荡的页面,没有任何引导。用户此时流失概率极高,等于你用一个空页面把最主动的一批访客赶走了。站内搜索的优化分三件事:让用户能方便地找到搜索框、让搜出来的结果真的相关、让搜不到时有出路。本文按这三件事给出可落地的做法。

原理分析:默认搜索为什么”搜不准”

WordPress 的默认搜索由 WP_Query 的 s 参数驱动,最终拼成的 SQL 里包含多个 LIKE '%关键词%' 条件,作用字段只有标题和正文,排序固定为 post_date DESC(搜索时其实会用相关性做一次粗排,但权重非常弱,且不包含自定义字段、摘要、分类名)。这带来三个问题:

一是覆盖字段窄:产品型号写在自定义字段里、标签写在 tag 里,LIKE 扫不到,必然搜不出来。二是排序不合理:一个 2021 年发布的、标题里完整包含关键词的权威教程,会排在一个 2026 年正文里提了一嘴的短文后面。三是分词粗糙:中文没有空格,MySQL 的 LIKE 只能做子串匹配,”手机壳”和”手机 保护壳”无法互通。

问题 根因 解决思路
搜不到自定义字段内容 LIKE 只扫 title/content 用插件建索引,或 posts_clauses 扩展 SQL
排序与相关度无关 默认按日期/弱相关排序 标题命中加权,用 posts_search / 插件
无结果页空荡 主题只输出一句话 自建无结果模板,加热词与推荐
中文分词不灵 MySQL 无中文分词器 用 Relevanssi 或接 Elasticsearch

改造幅度取决于内容规模:500 篇以内、字段简单,用几十行过滤器就够了;超过 2000 篇、有 WooCommerce 产品或大量自定义字段,直接用 Relevanssi 或 SearchWP 建独立索引表,别自己造轮子。

保姆级实操

  1. 把搜索框放到真正会被用到的位置

搜索框的标准位置是:PC 端主导航右侧、移动端顶部(点击后展开全屏搜索)、无结果页正中、404 页面。别把它塞进页脚。移动端尤其重要——小屏用户没耐心翻导航。

  1. 限定搜索范围,排除不该被搜到的内容

默认的 ?s= 会把页面、附件甚至产品变体一起搜出来。用 pre_get_posts 限定为文章,并排除某些分类:

<?php
// 只在前台主查询、且是搜索时生效
add_action( 'pre_get_posts', function ( $query ) {
    if ( is_admin() || ! $query->is_main_query() ) {
        return;
    }
    if ( $query->is_search() ) {
        $query->set( 'post_type', array( 'post', 'page' ) );
        // 排除"公告"类目(slug 为 notice)
        $query->set( 'tax_query', array( array(
            'taxonomy' => 'category',
            'field'    => 'slug',
            'terms'    => array( 'notice' ),
            'operator' => 'NOT IN',
        ) ) );
        $query->set( 'posts_per_page', 15 );
    }
} );

// 让搜索也能命中自定义字段:改写 JOIN 与 WHERE
add_filter( 'posts_search', function ( $search, $query ) {
    if ( ! $query->is_main_query() || ! $query->is_search() || empty( $search ) ) {
        return $search;
    }
    global $wpdb;
    $term = $query->get( 's' );
    // 在标题匹配的基础上,追加 meta 值匹配(OR 关系)
    $search .= $wpdb->prepare(
        " OR EXISTS ( SELECT 1 FROM {$wpdb->postmeta} pm
          WHERE pm.post_id = {$wpdb->posts}.ID
            AND pm.meta_value LIKE %s ) ",
        '%' . $wpdb->esc_like( $term ) . '%'
    );
    return $search;
}, 10, 2 );
  1. 给”标题命中”加权排序

上一步解决了”搜得到”,这一步解决”排得对”。最轻量的做法是搜索时用 posts_orderby 把标题命中的排到前面:

<?php
add_filter( 'posts_orderby', function ( $orderby, $query ) {
    if ( is_admin() || ! $query->is_main_query() || ! $query->is_search() ) {
        return $orderby;
    }
    global $wpdb;
    $term = $wpdb->esc_like( $query->get( 's' ) );
    // 标题命中置顶,其次按日期
    return $wpdb->prepare(
        "CASE WHEN {$wpdb->posts}.post_title LIKE %s THEN 0 ELSE 1 END ASC,
         {$wpdb->posts}.post_date DESC",
        '%' . $term . '%'
    );
}, 10, 2 );
  1. 做一页真正有用的无结果页

在主题里建 search.php,判断 have_posts() 为假时输出兜底内容:显示用户搜的词、给出热门搜索词(可用 wp_tag_cloud() 或直接写死一组)、列出最新 5 篇和精选 5 篇、放一个搜索框。模板骨架:

<?php get_header(); ?>
<div class="search-page">
  <form role="search" method="get" class="search-form" action="<?php echo esc_url( home_url( '/' ) ); ?>">
    <label class="screen-reader-text" for="s2">搜索</label>
    <input type="search" id="s2" class="search-field" name="s"
           value="<?php echo esc_attr( get_search_query() ); ?>" placeholder="换个词再试试">
    <button type="submit">搜索</button>
  </form>

  <?php if ( have_posts() ) : ?>
    <?php while ( have_posts() ) : the_post(); ?>
      <?php get_template_part( 'template-parts/content', 'search' ); ?>
    <?php endwhile; the_posts_pagination(); ?>
  <?php else : ?>
    <p class="search-empty">没有找到与「<strong><?php echo esc_html( get_search_query() ); ?></strong>」相关的内容。</p>
    <h2>试试这些高频词</h2>
    <?php wp_tag_cloud( array( 'smallest' => 14, 'largest' => 14, 'number' => 20, 'unit' => 'px' ) ); ?>
    <h2>或者从这里开始</h2>
    <ul>
      <?php
      $recent = new WP_Query( array( 'posts_per_page' => 5, 'ignore_sticky_posts' => true ) );
      while ( $recent->have_posts() ) : $recent->the_post(); ?>
        <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
      <?php endwhile; wp_reset_postdata(); ?>
    </ul>
  <?php endif; ?>
</div>
<?php get_footer(); ?>
  1. 记录用户到底在搜什么

在搜索表单提交时把关键词记到自定义表或一个 option 里,两周后看一遍:搜不到结果的高频词,就是下一个该写的内容选题。这是成本最低、命中率最高的选题来源。

  1. 规模上去了就换插件建索引

内容超过 2000 篇或需要中文分词时,装 Relevanssi(免费版即可做相关度排序与中文模糊匹配)或 SearchWP(付费,支持自定义字段权重与 WooCommerce)。这类插件会自建索引表,第一次配置后要点一次 “Build index”,之后自动增量更新。

常见踩坑

  • pre_get_posts 忘了判 is_main_query():把后台文章列表、侧边栏小工具一起改了,出现莫名其妙的空白。
  • 案例:某站点在搜索结果里用了 $wp_query->found_posts 做分页,但代码里另外跑了一个 query_posts(),导致分页总数和实际列表对不上,第二页永远是空的。正确做法是用 pre_get_posts 改主查询,永远不要在前台用 query_posts()。
  • 搜索结果页被 noindex:有的 SEO 插件默认把搜索结果页设为 noindex,这是对的,但别忘了它同时会让用户搜到的内容不被收录——这是预期行为,不要去改。
  • posts_search 拼接 SQL 没用 $wpdb->prepare():直接把用户输入的关键词拼进 SQL,等于开了一个 SQL 注入口子。
  • 无结果页直接跳回首页:用户以为站点坏了,跳出率极高。
  • 搜索框做成点击后异步加载,但 JS 报错:移动端点了没反应,等于没有搜索功能。上线前一定要在真机上测一次。
  • 装了搜索插件却没建索引:搜出来还是老样子,以为是插件没生效。

总结

站内搜索的判断标准是三个:搜核心词能不能在前 3 条找到最相关的那篇、搜错词时有没有出路、搜索框在小屏上能不能一秒找到。都满足的搜索能把停留时长和页面深度拉高一大截。改造顺序建议先做无结果页(半天工作量、收益最明显),再做排序加权,最后才考虑上插件。搜索优化完后,别忘了把搜索记录里的高频词接到 冷启动新站的 90 天内容计划 里当作选题输入,同时参考 网站移动端适配的验收标准 检查小屏搜索框的点击区域。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。