2026年9月6日
2026年9月6日
WordPressの検索機能エラーを解決する方法
はじめに
WordPressの検索フォームで検索すると結果が0件になる・検索するとホームページにリダイレクトされてしまう・カスタム投稿タイプが検索結果に出てこない・search.phpテンプレートが適用されずindex.phpが表示される・日本語の検索が正しく機能しないといった問題は、WordPressの検索処理の仕組みを正しく理解することで解決できます。
症状・原因
- パーマリンク設定が「プレーン」になっており
?s=クエリが正しく処理されない - カスタム投稿タイプ登録時に
exclude_from_searchをtrueに設定している pre_get_postsフックで検索クエリを誤って変更している- テーマの
search.phpが存在しないか、テンプレート階層が正しく解決されていない
解決手順
ステップ1:検索機能の状態を診断する
# 検索クエリが正しく動作するか確認
wp eval "
\$q = new WP_Query(['s' => 'テスト', 'post_type' => 'any']);
echo '検索結果: ' . \$q->found_posts . '件' . PHP_EOL;
echo 'SQL: ' . \$q->request . PHP_EOL;
"
# カスタム投稿タイプの exclude_from_search 設定を確認
wp eval "
global \$wp_post_types;
foreach (\$wp_post_types as \$slug => \$type) {
if (!\$type->exclude_from_search) {
echo \$slug . ': 検索に含まれる' . PHP_EOL;
}
}
"
# search.php テンプレートの存在確認
wp eval "echo locate_template(['search.php']);"
# パーマリンク設定を確認
wp option get permalink_structure
// 検索クエリのデバッグ
add_action('pre_get_posts', function(WP_Query $query): void {
if ($query->is_search()) {
error_log('Search query: ' . $query->get('s'));
error_log('Post types: ' . print_r($query->get('post_type'), true));
error_log('Is main query: ' . ($query->is_main_query() ? 'yes' : 'no'));
}
});
ステップ2:検索クエリを拡張する
// カスタム投稿タイプを検索対象に追加
add_action('pre_get_posts', function(WP_Query $query): void {
if (!$query->is_main_query() || !$query->is_search() || is_admin()) {
return;
}
// 検索対象の投稿タイプを設定
$query->set('post_type', ['post', 'page', 'product', 'event']);
// 検索結果の件数を設定
$query->set('posts_per_page', 20);
// カスタムフィールドも検索対象に含める
$query->set('meta_query', [
'relation' => 'OR',
// 通常の検索(デフォルト)に加えてメタも検索
]);
});
// カスタムフィールドも検索対象に含める(posts_search フィルター)
add_filter('posts_search', function(string $search, WP_Query $query): string {
if (!$query->is_search() || !$query->is_main_query()) {
return $search;
}
global $wpdb;
$term = $query->get('s');
if (empty($term)) {
return $search;
}
$like = '%' . $wpdb->esc_like($term) . '%';
$search .= $wpdb->prepare(
" OR EXISTS (
SELECT 1 FROM {$wpdb->postmeta}
WHERE {$wpdb->postmeta}.post_id = {$wpdb->posts}.ID
AND {$wpdb->postmeta}.meta_key NOT LIKE '\\_%%'
AND {$wpdb->postmeta}.meta_value LIKE %s
)",
$like
);
return $search;
}, 10, 2);
ステップ3:検索テンプレートをカスタマイズする
// search.php(テーマに配置)
// テンプレート階層: search.php → index.php
// 検索フォームのカスタマイズ(searchform.php)
// テーマの searchform.php が優先される
function my_custom_searchform(string $form): string {
return '<form role="search" method="get" action="' . esc_url(home_url('/')) . '">
<label for="search-field" class="screen-reader-text">検索</label>
<input type="search" id="search-field" name="s"
value="' . get_search_query() . '"
placeholder="キーワードを入力..."
aria-label="サイト内検索">
<select name="post_type">
<option value="">すべて</option>
<option value="post" ' . selected(get_query_var('post_type'), 'post', false) . '>記事</option>
<option value="product" ' . selected(get_query_var('post_type'), 'product', false) . '>商品</option>
</select>
<button type="submit">検索</button>
</form>';
}
add_filter('get_search_form', 'my_custom_searchform');
// 検索結果ページのタイトル
add_filter('document_title_parts', function(array $title): array {
if (is_search()) {
$title['title'] = sprintf('「%s」の検索結果', get_search_query());
}
return $title;
});
ステップ4:日本語検索を改善する
// 日本語の全文検索を改善(形態素解析なしでの工夫)
add_filter('posts_search', function(string $search, WP_Query $query): string {
if (!$query->is_search()) {
return $search;
}
$term = $query->get('s');
// 日本語の場合はスペースで分割して各語を AND 検索
$terms = preg_split('/[\s ]+/u', trim($term), -1, PREG_SPLIT_NO_EMPTY);
if (count($terms) <= 1) {
return $search;
}
global $wpdb;
$conditions = [];
foreach ($terms as $t) {
$like = '%' . $wpdb->esc_like($t) . '%';
$conditions[] = $wpdb->prepare(
"({$wpdb->posts}.post_title LIKE %s OR {$wpdb->posts}.post_content LIKE %s)",
$like,
$like
);
}
return ' AND (' . implode(' AND ', $conditions) . ')';
}, 10, 2);
// 検索ハイライトを追加
add_filter('the_excerpt', function(string $excerpt): string {
if (!is_search()) {
return $excerpt;
}
$term = get_search_query();
if (empty($term)) {
return $excerpt;
}
$pattern = '/' . preg_quote($term, '/') . '/ui';
return preg_replace($pattern, '<mark>$0</mark>', $excerpt);
});
ステップ5:検索をREST APIと連携させる
// REST API で検索を提供(フロントエンドSPA対応)
add_action('rest_api_init', function(): void {
register_rest_route('myplugin/v1', '/search', [
'methods' => WP_REST_Server::READABLE,
'callback' => function(WP_REST_Request $request): WP_REST_Response {
$term = sanitize_text_field($request->get_param('q') ?? '');
$post_type = sanitize_key($request->get_param('type') ?? 'any');
if (empty($term)) {
return new WP_REST_Response(['results' => [], 'total' => 0], 200);
}
$query = new WP_Query([
's' => $term,
'post_type' => $post_type,
'posts_per_page' => 10,
'post_status' => 'publish',
'no_found_rows' => false,
]);
$results = array_map(fn($p) => [
'id' => $p->ID,
'title' => $p->post_title,
'excerpt' => wp_trim_words($p->post_excerpt ?: $p->post_content, 20),
'url' => get_permalink($p->ID),
'type' => $p->post_type,
], $query->posts);
return new WP_REST_Response([
'results' => $results,
'total' => $query->found_posts,
], 200);
},
'permission_callback' => '__return_true',
'args' => [
'q' => ['required' => true, 'sanitize_callback' => 'sanitize_text_field'],
'type' => ['default' => 'any', 'sanitize_callback' => 'sanitize_key'],
],
]);
});
注意事項
posts_searchフィルターでカスタムフィールドを検索対象に追加すると、テーブルJOINが増えてクエリが重くなります。投稿数が多いサイトでは、Elasticsearch(ElasticPressプラグイン)や専用の検索インデックステーブルの導入を検討してくださいpre_get_postsフックは管理画面のクエリにも影響します。必ずis_admin()チェックとis_main_query()チェックを組み合わせて、フロントエンドのメインクエリのみに適用してください
まとめ
WordPress検索問題の解決は①WP_Query(['s'=>'テスト'])で検索SQLを確認・$wp_post_typesでexclude_from_search設定を確認・pre_get_postsフックでデバッグログ出力、②pre_get_postsでpost_typeに検索対象を追加・posts_searchフィルターでカスタムフィールドもLIKE検索・posts_per_pageで件数を設定、③search.phpテンプレートを作成・get_search_formフィルターで独自フォームを実装・post_type選択UIで絞り込み機能を追加、④日本語スペース区切りでAND検索・the_excerptフィルターで検索語をハイライト表示・全角スペース対応の正規表現、⑤REST APIで/myplugin/v1/searchエンドポイントを作成・フロントエンドSPA向けにJSON形式で検索結果を返却の手順で解決します。