Make a Useful WordPress 404 Page

The standard WordPress 404 page is next to useless. Like most 404 pages, the basic WordPress one shows a ‘Nothing found’ message and a search box. With a few quick tweaks we can make your 404 page useful, like ours.


The requirements to make a custom 404 page in WordPress are simple:

404 Solution is a must-have for every WordPress website. This plugin captures requests for non existent posts and pages and either sends the visitor to a post with a page slug or title that matches the requested page slug best or, if no appropriate match is found, the plugin logs the request and sends the visitor to a 404 page. All redirects can be managed through 404 Solution’s settings page. The 404 page visitors might be sent to can be the site’s default 404 page or any page configured within 404 Solution’s options.

404 Solution is a handy way to detect missing pages and to detect the type of content visitors might like to read.

Jetpack is an optional requirement when building a custom 404 page. Jetpack has a Shortcode’s Embed module which has shortcodes that display an HTML sitemap that shows visitors posts they might like to read, Top Posts that visitors already like to read, and archive lists which show links to posts that meet customizable criteria.

The Code Snippet adds a shortcode to use to display a search form and a shortcode to use to list taxonomy term titles and links to posts filed under each term.

Create the 404 Page

The general flow of the instructions is as follows. Detailed step-by-step details are further down the page.

  • Install 404 Solution. Go to Dashboard > Plugins and search for 404 Solution in the WordPress plugin repository.
  • Install the Post List and Search Form shortcode code snippet shown below here. Put the code into your theme’s functions.php file. More instructions are provided with the code snippet (see below).
  • Create a 404 page. Go to Dashboard > Pages > Add New.
  • Put the 404 Page Template HTML shown below here into the page editor. Switch the editor to Text/Code View mode, paste in the template code then switchback to Visual mode.
  • Optional. Install Jetpack if you want to display your site’s Top Ten Posts. If your site is furnished with another plugin that has a Top Posts or Most Shared Posts shortcode you could use its shortcode instead.
  • Configure 404 Solution to use the custom 404 page.



Install 404 Solution from the WordPress plugin repository.


Install the Post List shortcode snippet and Search Form shortcode snippet. They are shown in the code block under here.

If you use a child theme you can safely install the code snippets into your child theme’s functions.php file. The code goes at the bottom of the file. Go to Appearance > Editor and select functions.php.

If you do not use a child theme you will need to install the My Custom Functions plugin, or a similar plugin that makes it easy to safely add code snippets into WordPress sites.

After installation of My Custom Functions go to Dashboard > Settings > PHP Inserter then add the code snippet. Remember to enable the snippet and save the document.

Some themes come with features that let admins add code snippets into sites. GeneratePress Pro has the Elements feature, for example.

The shortcode snippet.


// Shortcode: Search Form
function shortcode_search_form_vr51() {
    $content = ob_get_clean();
	return $content;
add_shortcode('search', 'shortcode_search_form_vr51');

* Shortcode: Taxonomy Terms List
* Copyright Lee Hodson <>
*	[ postlist]
*	[ postlist tax='' exc='' max='' sort='' ord='']
*	tax: Taxonomy to show. Comma separated WordPress internal taxonomy name or taxonomy IDs.
*	exc: Terms to exclude from the returned list.
*	max: Maximum number of posts to retrieve for each term.
*	sort: How the posts returned should be organized.
*	ord: Whether to display the organised list in ASCending or DESCending order.
*	debug: Disable results caching. 1=Disable
function shortcode_list_terms_and_posts_vr51( $atts ) {

	$atts = shortcode_atts(array(
		'tax' =>	'category', // Taxonomies to show. ID or WordPress internal term designation.
		'exc' =>	'', // List of terms to exclude. Use numerical ID, not slug or title.
		'max' =>	'10', // Maximum number of posts to show under each term. Use -1 to show all.
		'sort' =>	'title', // Sort method: none, ID, author, title, name, type, date, modified, parent, rand, comment_count, menu_order, post__in. Not implemented: meta_value => '', meta_key => ''. Not implemented meta_value_num => '', meta_key => ''
		'ord' =>	'ASC', // Order: ASC, DESC
		'debug' => '0' // Debug flag

	), $atts, 'postlist' );

	// Store sanitized shortcode attribute values in variables
	$tax = explode( ',', sanitize_text_field($atts['tax']) );
	$exc = explode( ',', intval($atts['exc']) );
	$max = intval($atts['max']);
	$sort = sanitize_text_field($atts['sort']);
	$ord = sanitize_text_field($atts['ord']);
	$debug = intval($atts['debug']);

	// Check for cached results
	// Get an MD5 hash of the attribute values
	// Shortcode attrib combinations are likely to yield the same 32 character hash only when they are configured to return the same post list result
	// If the same hash has already been calculated then the results related to that hash should be retrieved from the database or RAM cache, wherever transients are stored.

	// $atts['pid'] = get_the_ID(); // Page ID is unimportant for this particular shortcode. The results are not affected by the page.
	$id = md5( serialize( $atts ) ); // The hash

	if ( 1 === $debug ) {
		// Always return uncached results f debug is enabled
		delete_transient( 'shortcode_list_terms_and_posts_vr51'.$id );

	// Retrieve cached results if the exist
	$cache = get_transient( 'shortcode_list_terms_and_posts_vr51'.$id );
	if ( ! empty($cache) ) {
		return $cache;

	} else {
		// Calculate fresh content then cache it in a transient

		// Confirm the listed taxonomies exist. Prevents errors later.
		foreach ( $tax as $t ) {
			if ( taxonomy_exists( $t ) ) {
				$taxes[] = $t;

		// Get the terms that belong to each taxonomy
		$terms = get_terms( $taxes, array(
			'orderby'			=>	'name',
			'hide_empty'	=>	true,
			'exclude'			=>	$exc
		) );

		// Use output buffering to control the output
			// Display the terms of each taxonomy and list under each term the posts registered to it
			foreach ($terms as $t) {

				if ( is_object( $t ) ) {

					// Filter all posts in the given taxonomy for the requested terms
							'tax_query' => array(
											'taxonomy' => $t->taxonomy,
											'terms' => $t->term_id,
							'orderby' => "$sort",
							'order' => "$ord",
							'posts_per_page'	=>	"$max"

					// Term title
					echo "<h3 style='margin-top:25px;'>".$t->name."</h3>";

					// Term posts
					echo '<ul>';
					while(have_posts()) {
						$terms = get_the_terms( get_the_ID(), $t->name );

						echo '<li style="list-style-type:none;"><a href="'.get_permalink().'">'.get_the_title().'</a></li>';

					echo '</ul>';

					wp_reset_query(); // Reset Query

		$content = ob_get_clean();
		set_transient( 'shortcode_list_terms_and_posts_vr51'.$id, "$content", 60*60*24 );
		$d = get_transient( 'shortcode_list_terms_and_posts_vr51'.$id );

		return $content;

	return; // No cache or no result? Then return empty handed



Make your custom 404 Page. Create a page. Give it any title you like. Obvious names include 404, Error 404 and 404 Not Found.

Copy the page template shown below into the page editor’s text mode then switch back to visual.

The 404 Page template code is written in the new WordPress Gutenberg editor. It works in both the normal (classic) TinyMCE editor and in Gutenberg.

Switch editor mode: Gutenberg

Switch editor mode: Gutenberg

Switch editor mode: TinyMCE

Switch editor mode: TinyMCE

How the template looks in the Gutenberg visual editor

The Custom 404 Page Template

<!-- wp:paragraph -->
<p>The post you came to see might have moved. Here are some suggestions.</p>
<!-- /wp:paragraph -->

<!-- wp:shortcode -->
[ search]
<!-- /wp:shortcode -->

<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

<!-- wp:shortcode -->
[ abj404_solution_page_suggestions]
<!-- /wp:shortcode -->

<!-- wp:shortcode -->
[ jetpack_top_posts_widget]
<!-- /wp:shortcode -->

<!-- wp:heading -->
<h2 id="sitemap">All Posts</h2>
<!-- /wp:heading -->

<!-- wp:shortcode -->
[ postlist max='-1']
<!-- /wp:shortcode -->

Shortcode: Search Form

The search form shortcode has no additional options. Simply enter it into a post or page as [ search ] (without the spaces).

Shortcode: Post List

The post list shortcode displays all the category level terms (so not tags) used by a post type. Below each term is a list of posts filed under it. By default the [ postlist ] shortcode shows all categories and the posts within those categories

The options are:

  • tax=’taxonomy’
  • exc=’terms-to-not-list’
  • max=’number-of-posts-to-show’
  • sort=’criteria-to-arrange-posts-by’
  • ord=’whether-to-list-in-ascending-or-descending-sort-order’

tax=” must be the numerical ID of the taxonomy or the WordPress internal name for the taxonomy. For example, tax=’category’ or tax=’wprm_course’ or tax=’category,wprm_course’. The default taxonomy is ‘category’.

exc=” must be the numerical ID or IDs of any term(s) to exclude from the list of terms displayed. For example, if the category term ‘Uncategorised’ has the ID of ’65’ then exc=’65’ would stop Uncategorized posts being shown in the list of posts displayed.

max=” must be a number. This is the maximum number of posts to show under each taxonomy term. The default is max=’10’. Set to max=’-1′ to display all posts found.

sort=” must be one of none, ID, author, title, name, type, date, modified, parent, rand, comment_count, menu_order, post__in. This determines how the posts will be arranged when listed. sort=’title’ arranges posts by post title. sort=’name’ arranges posts by post slug. The default is title.

ord=” determines whether the post list is sorted in ascending or descending order. ord=’ASC’ sorts posts in ascending order. ord=’DESC’ sorts post in descending order. The default is ASCending order.

The shortcode caches results to reduce database requests.


Install Jetpack. This is optional. The custom 404 page template shown above includes the Jetpack Top Posts shortcode [ jetpack_top_posts_widget ].

If you want to display your site’s top posts in your 404 page you will need to use either Jetpack or another plugin that includes a top posts feature.

Social Pug could be used instead of Jetpack. It has a Most Shared shortcode.

If it’s not needed, remove the Jetpack shortcode from the template if you prefer to not use the Jetpack Top Posts widget in your custom 404 page.


Tell 404 solution to use your custom 404 page template. Go to Settings > 404 Solution > Options and select your site’s new custom 404 page.

Select your custom 404 Page template in the Options page of 404 Solution.

Further down the options page is a section to use to control the display of the page suggestions shown by the 404 Solution page recommendations shortcode. Use it to set the title shown above page recommendations.

The Result

The result is a custom 404 page that helps visitors who stumble across it to find useful pages in your site. Pages that might interest them. Pages that they might share with others. Better yet, pages they might see ads on and click through to buy something from a sponsor.

Here is our 404 Page

Isn’t it a beauty!

Sharing is caring!

Notify of

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Inline Feedbacks
View all comments
Would love your thoughts, please comment.x