WordPress: How To Exclude Page Paths (and Sub-Pages) from Internal Site Search

I designed, built, and launched a new WordPress site for my company. As part of the new site, we have some embedded videos that we are not allowed to have for public consumption but that can be used in 1:1 sales and partner conversations. We didn’t want to gate the videos, but we did want to ensure they wouldn’t wind up on other sites or in search engines.
To ensure this, I controlled the hosting and SEO settings for the parent page and associated subpages.
- I hosted the videos on Wistia where we could control their access and external embedding to our domain only.
- I built a private page on our site with a table of contents, then had each video in a subpage below it.
- Each page was marketed as no-index and excluded from the XML sitemap using Rank Math.
- As a precaution, I also added the path in our robots.txt file with a disallow command.
WordPress Internal Search
Those precautions would stop the videos from being externally crawled and indexed by search engines. However, there was still one way that visitors could inadvertently find the pages… using the site’s internal search. WordPress doesn’t offer a means of excluding pages from internal search, so I built a function and saved it in a custom plugin I built for the site.
In the code below, I utilize the path private
as an example. That would be the slug for the parent page and every page below it would be excluded from internal searches.
<?php
/**
* Plugin Name: Exclude Custom Pages from Search
* Description: Excludes pages under specified paths (and their subpages) from internal search results.
* Version: 2.0
* Author: Douglas Karr
* Author URI: https://dknewmedia.com
* License: GPL2
*/
/**
* Array of path(s) to exclude.
*
* Add the slug(s) of the pages you wish to exclude, along with all their descendant pages.
*/
$excluded_paths = array(
'private'
);
/**
* Helper function to determine if the current page is one of the excluded pages or a descendant thereof.
*
* @return bool True if the current page is under an excluded path; false otherwise.
*/
function is_excluded_page() {
global $post, $excluded_paths;
if ( ! $post ) {
return false;
}
// Loop through each excluded path.
foreach ( $excluded_paths as $path ) {
// Retrieve the page by its slug.
$page = get_page_by_path( $path );
if ( $page ) {
// Get all ancestor IDs of the current page.
$ancestors = get_post_ancestors( $post->ID );
// Check if the current page is either the page with the given path or a descendant.
if ( $post->ID === $page->ID || in_array( $page->ID, $ancestors, true ) ) {
return true;
}
}
}
return false;
}
/**
* Exclude the specified pages from internal search results.
*
* This function hooks into the 'pre_get_posts' filter and, when a front-end search query runs,
* it removes any pages that match an excluded path (and their descendants) from the results.
*
* @param WP_Query $query The current query.
* @return WP_Query The modified query.
*/
function exclude_custom_pages_from_search( $query ) {
global $excluded_paths;
if ( $query->is_search() && ! is_admin() && $query->is_main_query() ) {
$exclude_ids = array();
// Loop through each excluded path.
foreach ( $excluded_paths as $path ) {
// Retrieve the page with the specified slug.
$page = get_page_by_path( $path );
if ( $page ) {
// Exclude the page itself.
$exclude_ids[] = $page->ID;
// Retrieve all descendant pages.
$descendants = get_pages( array(
'child_of' => $page->ID,
'post_type' => 'page',
'post_status' => 'publish',
) );
foreach ( $descendants as $descendant ) {
$exclude_ids[] = $descendant->ID;
}
}
}
// Merge with any existing exclusions if necessary.
$existing_excludes = $query->get( 'post__not_in' );
if ( ! empty( $existing_excludes ) && is_array( $existing_excludes ) ) {
$exclude_ids = array_merge( $existing_excludes, $exclude_ids );
}
// Apply the exclusions to the search query.
$query->set( 'post__not_in', $exclude_ids );
}
return $query;
}
add_filter( 'pre_get_posts', 'exclude_custom_pages_from_search' );
This is a complete solution for excluding specific pages and their descendants from internal WordPress search.
How This Plugin Works
- Defines an array of excluded paths (
$excluded_paths
) at the top of the file. You can add more slugs to this array to exclude multiple pages. - Determines if a page should be excluded via the
is_excluded_page()
function checks whether the current page matches one of the specified slugs or is a descendant of an excluded page. - Modifies the search query using
pre_get_posts
to remove excluded pages and their children from appearing in search results. - Ensures that exclusions are merged with any existing exclusions in the
post__not_in
query parameter.
This plugin correctly modifies the main search query so that excluded pages and all of their child pages do not appear in search results.
Potential Enhancements
- User Interface: This could be added as a panel on pages, posts, and custom post types. Or a settings page could be added where an array of paths could be added.
- Dynamic Slug Management: Instead of hardcoding
$excluded_paths
in the file, you could create a settings page in the WordPress admin area to allow users to modify the excluded paths dynamically. - Logging or Debugging: If you need to troubleshoot whether a page is being excluded properly, add debug logs to check the
$exclude_ids
array would be helpful. - Handling Non-Page Post Types: This is specifically designed for
page
post types. You might need to adjust if your site includes custom post types (CPTs) that follow a similar hierarchy.
To use this code, add a folder to your plugins folder (eg. exclude-from-search
) and then copy the code above to a PHP file within the folder (eg. exclude-from-search.php
). Activate the plugin, and your pages will be excluded from internal searches.