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.

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

  1. 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.
  2. 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.
  3. Modifies the search query using pre_get_posts to remove excluded pages and their children from appearing in search results.
  4. 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

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.

Exit mobile version