
Add to your theme's functions.php file a hook on parse_query that turns every search query into a 404, a get_search_form filter that returns an empty string, and remove the search widget and block. The complete code, tested on WordPress 7.1, is in the article; no plugin is needed.
A five-page brochure site, a one-page site, a portfolio: WordPress search is of no use there, and its form clutters the theme. Worse, the results page exists even without a form, at /?s=mot, and it exposes either an empty page or a list of content you never meant to put forward. Here is how to disable WordPress search completely, without a plugin, with a snippet for functions.php.
Why disable search
- It is useless on a small site: everything is one click away in the menu.
- It produces worthless results pages, which bots can request with any word at all, wasting server resources and creating junk URLs.
- The form imposed by the theme often has no place in the design.
The complete code for functions.php
Put it in the functions.php file of your child theme (or in a small plugin of your own, so that it survives a change of theme). Each block closes a different door.
<?php
/**
* Disables WordPress search on the public side.
*/
// 1. Any search by URL (/?s=mot or /search/mot/) becomes a 404 page.
add_action( 'parse_query', function ( WP_Query $query ) {
if ( is_admin() || ! $query->is_main_query() || ! $query->is_search() ) {
return;
}
$query->is_search = false;
$query->set_404();
status_header( 404 );
nocache_headers();
} );
// 2. The theme's search form is no longer displayed.
add_filter( 'get_search_form', '__return_empty_string' );
// 3. The classic search widget is removed.
add_action( 'widgets_init', function () {
unregister_widget( 'WP_Widget_Search' );
}, 11 );
// 4. The editor's “Search” block disappears from the inserter.
add_filter( 'allowed_block_types_all', function ( $allowed ) {
if ( ! is_array( $allowed ) ) {
$registry = WP_Block_Type_Registry::get_instance();
$allowed = array_keys( $registry->get_all_registered() );
}
return array_values( array_diff( $allowed, [ 'core/search' ] ) );
} );
// 5. The search REST endpoint is closed.
add_filter( 'rest_endpoints', function ( array $endpoints ) {
unset( $endpoints['/wp/v2/search'], $endpoints['/wp/v2/search/(?P<id>[\d]+)'] );
return $endpoints;
} );What each block does:
- The query. On
parse_query, if the main query is a search on the public side, it is turned into a 404 withset_404()and the matching HTTP status code.is_admin()leaves the admin search untouched;is_main_query()avoids interfering with plugins’ secondary queries. - The form.
get_search_form()is the function themes call to display the form; the filter makes it return an empty string. - The widget of classic themes.
- The block of block-based themes. The filter receives either
true(all blocks allowed) or a list: in the first case, the full list is rebuilt beforecore/searchis removed from it. - The REST API.
/wp-json/wp/v2/search?search=motnow returns arest_no_routeerror instead of a list of results.
Checking
Three requests are enough, in a browser or with curl:
curl -s -o /dev/null -w "%{http_code}\n" "https://exemple.fr/?s=test"
# 404
curl -s "https://exemple.fr/wp-json/wp/v2/search?search=test" | head -c 120
# {"code":"rest_no_route","message":"No route was found matching the URL and request method."…
curl -s "https://exemple.fr/" | grep -c 'role="search"'
# 0: no form left in the page (if the theme goes through get_search_form)In the admin, searching posts, pages and media must keep working.
If the theme displays its own form
Some themes write the form directly into their templates (header.php, searchform.php) without going through get_search_form(). The filter then has no effect on the display. Two options: override the template in question in a child theme, or hide the form with CSS in the meantime:
.search-form,
form[role="search"] {
display: none;
}The CSS only hides: blocking /?s= with the first PHP block remains essential.
Variant: redirecting ?s= in .htaccess
If you would rather send visitors to the home page than serve a 404, an Apache rule does it before WordPress even runs. It is restricted to the site root so as not to affect the admin search (wp-admin/edit.php?s=…).
# Place this before the # BEGIN WordPress block
RewriteEngine On
RewriteCond %{QUERY_STRING} (^|&)s= [NC]
RewriteRule ^$ /? [R=301,L]The trailing ? empties the query string of the redirect. This rule and the PHP code are not mutually exclusive: the first spares the server from running WordPress, the second remains the reference protection, including for /search/mot/ permalinks.
What about the Disable Search plugin?
It exists and does the same thing. On Gekkode, the editorial line is not to add a plugin for what a theme can do in thirty lines: one dependency fewer to update, and behaviour you can read in your own code. If you manage several sites without a child theme, the plugin remains a reasonable option.
In the same vein, restricting access to the WordPress dashboard and migrating a WordPress site are two other operations that need no plugin.
Common errors
is_search() test without is_admin() breaks search in the dashboard's post and media lists. The code in this article excludes the admin./?s=mot remains reachable by anyone who types the URL. It is the hook on parse_query that closes the door, not removing the form.s= with no path constraint also redirects wp-admin/edit.php?s=…. Restrict the rule to the site root.get_search_form(), the filter does not remove it. You then have to edit the theme's template (in a child theme, preferably).

