Jesin's Blog

Welcome to the Portal of Technology

  • Facebook
  • GitHub
  • RSS
  • Twitter
  • Home
  • Categories
    • Domains
    • Linux
    • Networking
    • PHP
    • Virtualization
    • Web Design
    • Web Servers
    • Windows
  • WordPress Plugins
    • Custom Error Pages
    • HTTP Digest Authentication
    • Mailgun Email Validator
  • Toolbox
    • DNS Lookup Tool
    • htdigest Generator Tool Online
    • htpasswd Generator Tool Online
    • HTTP Headers Lookup Tool
    • MD5 Encryption Tool
    • Open Port Check Tool
    • SHA-1 Encryption Tool
    • URL Encoding/Decoding Tool
  • About Me
  • Contact Me
  • Sitemap
Home ›
PHP ›
Custom 403 and 401 error pages in WordPress

Custom 403 and 401 error pages in WordPress

September 18, 2013 PHP Jesin A 5 Comments

wordpress custom 403 error page

Earlier I had written an article on custom error pages for Apache but doing the same with WordPress is not that straightforward. WordPress has the ability to handle 404s internally but doing the same for other 4xx errors requires modifying the code.

I’ve been searching how to do this for months without luck. Recently after I created my own theme for this blog I got some knowledge of WordPress internals.

I put that to use and after several hours of experimenting built the 403 and 401 custom error pages for my WordPress blog.

Update: I’ve developed a WordPress plugin for creating error pages without writing a single line of code. Find it here.

Step 1: Create a child theme

Creating a child theme will ensure that changes aren’t overwritten after the main theme is updated. If you’re creating a custom 403 page from your own WordPress theme you can skip this step.

To create a child theme

  1. Create a directory inside /document/root/wp-content/themes/ and name it <yourtheme>-child. So if you are doing this on Customizr theme create customizr-child.
  2. Inside /document/root/wp-content/themes/customizr-child create a file called style.css with the following contents
    /*
    Theme Name: Customizr Child
    Template: customizr
    */
    @import url('../customizr/style.css');

    While the Theme Name: can be anything the Template: field must match the directory name of the main theme.

  3. Login to the admin panel, go to Appearance > Themes and enable “Customizr Child” or whatever you entered for Theme Name.

Step 2: Create or Copy a 403.php file

Copy the 404.php file from the main theme to the child theme

cp wp-content/themes/customizr/404.php wp-content/themes/customizr-child/403.php

People with their own theme can create a copy inside the same directory

cp wp-content/themes/your-theme/404.php wp-content/themes/your-theme/403.php

For a custom 401 error page, name the file 401.php. If your theme doesn’t have a 404.php file read this on how to create one.

Step 3: Edit the functions.php file

Again people with their own themes edit your wp-content/themes/your-theme/functions.php file.

Those who created a child theme should create a wp-content/themes/customizr-child/functions.php file and place the following PHP code inside.

function custom_error_pages()
{
    global $wp_query;

    if(isset($_REQUEST['status']) && $_REQUEST['status'] == 403)
    {
        $wp_query->is_404 = FALSE;
        $wp_query->is_page = TRUE;
        $wp_query->is_singular = TRUE;
        $wp_query->is_single = FALSE;
        $wp_query->is_home = FALSE;
        $wp_query->is_archive = FALSE;
        $wp_query->is_category = FALSE;
        add_filter('wp_title','custom_error_title',65000,2);
        add_filter('body_class','custom_error_class');
        status_header(403);
        get_template_part('403');
        exit;
    }

    if(isset($_REQUEST['status']) && $_REQUEST['status'] == 401)
    {
        $wp_query->is_404 = FALSE;
        $wp_query->is_page = TRUE;
        $wp_query->is_singular = TRUE;
        $wp_query->is_single = FALSE;
        $wp_query->is_home = FALSE;
        $wp_query->is_archive = FALSE;
        $wp_query->is_category = FALSE;
        add_filter('wp_title','custom_error_title',65000,2);
        add_filter('body_class','custom_error_class');
        status_header(401);
        get_template_part('401');
        exit;
    }
}

function custom_error_title($title='',$sep='')
{
    if(isset($_REQUEST['status']) && $_REQUEST['status'] == 403)
        return "Forbidden ".$sep." ".get_bloginfo('name');

    if(isset($_REQUEST['status']) && $_REQUEST['status'] == 401)
        return "Unauthorized ".$sep." ".get_bloginfo('name');
}

function custom_error_class($classes)
{
    if(isset($_REQUEST['status']) && $_REQUEST['status'] == 403)
    {
        $classes[]="error403";
        return $classes;
    }

    if(isset($_REQUEST['status']) && $_REQUEST['status'] == 401)
    {
        $classes[]="error401";
        return $classes;
    }
}

add_action('wp','custom_error_pages');

I’ve highlighted the lines which can be customized according to your liking. This will be displayed in between the <title> </title> tags.

Save the functions.php file and test it by adding a ?status query string like

http://www.example.com/?status=403

http://www.example.com/?status=401

This should display a page similar to a 404 page. Do this test BEFORE customizing the 403.php or 401.php file and proceed only if this succeeds because

To error is human

When editing the 401.php or 403.php file if you commit a mistake doing this test will show the infamous WordPress white-screen-of-death. Troubleshooting it can become confusing which is why you should test before editing them.

The explanation of this code is give at the end.

Step 4: Customize the 401.php or 403.php file

Once you find that things work after editing the functions.php file go ahead and customize to your heart’s content.

Each theme differs so I cannot give very specific instructions on this. Since we are using the Customizr theme for this example I’ll share the code for it.

<?php do_action( '__before_main_wrapper' ); ##hook of the header with get_header ?>
<?php tc__f('rec' , __FILE__ , __FUNCTION__ ); ?>
<div id="main-wrapper" class="container">
   <div class="container" role="main">
        <div class="row">
            <?php do_action( '__before_article_container'); ?>
                <div class="<?php echo tc__f( '__screen_layout' , tc__f ( '__ID' ) , 'class' ) ?> article-container">
                    <header><h1 class="entry-title">403 Forbidden</h1></header>
			<div class="entry-content"><p>Access to this area is forbidden. Please go to the home page.</p></div>
		</div><!--.article-container -->
	   <?php do_action( '__after_article_container'); ?>
        </div><!--.row -->
    </div><!-- .container role: main -->
<?php do_action( '__after_main_container' ); ?>
</div><!--#main-wrapper"-->
<?php do_action( '__after_main_wrapper' ); ?>

The modified lines are highlighted.
Normally a 404.php page contains some static text and a get_search_form() function. Remove the function and replace the text with your own words.

Step 5: Set the Error Document on your web server

I’ve used only Apache and Nginx so I’ll provide steps for them. For the documentation for other web servers.

Apache

Edit the .htaccess file or your vhosts file and add the ErrorDocument directive as appropriate.

ErrorDocument 403 /index.php?status=403
ErrorDocument 401 /index.php?status=401

If you’re hosting WordPress inside a directory you should add that too like

ErrorDocument 403 /blog/index.php?status=403

Nginx

Edit the nginx virtual host configuration file of WordPress inside /etc/nginx/sites-enabled and use the error_page directive

error_page 403 = /index.php?status=403
error_page 401 = /index.php?status=401

Do a config test

service nginx configtest

and reload

service nginx reload

Now test it by accessing something that is forbidden.

I have Directory Listing disabled in my web server so if I access /wp-includes/ it displays my WordPress custom 403 page.

Similarly I have password protected the wp-login.php file so accessing it will show my WordPress custom 401 page.

Digging into the code

Now for the working of this code you added to the functions.php file.

I’ll start explaining the last line first because this is how execution starts.

add_action('wp','custom_error_pages');

The add_action() hooks a function into a specific action. So here we are hooking the user defined function custom_error_pages() to the action wp.

Many people recommend hooking into the template_redirect action as that is when WordPress decides which template to load. But for this purpose I find it a little too late as the headers are already sent.

Then why not use an action much earlier? I tried that too but it was a bit too earlier as the WP Query object wasn’t set. So the wp action seems just right.

Let’s move to our function custom_error_pages()

global $wp_query;

This is to make the global object $wp_query available inside our function.

if(isset($_REQUEST['status']) && $_REQUEST['status'] == 403)

The condition which checks if the URL contains the query string ?status=403. But why have two conditions – isset() and the comparison operator isn’t the second one enough?

Yes it is enough but checking that way shows a PHP notice that index “status” is undefined. Even though it isn’t shown onscreen on define(‘WP_DEBUG’,false) environments it is better to have it. Now going inside the condition.

$wp_query->is_404 = FALSE;
$wp_query->is_page = TRUE;
$wp_query->is_singular = TRUE;
$wp_query->is_single = FALSE;
$wp_query->is_home = FALSE;
$wp_query->is_archive = FALSE;
$wp_query->is_category = FALSE;

In this chunk of code we are saying that the template displayed is for a singular page and not a post, home, archive or category. This is required in themes like Customizr which use only the index.php file for all types of templates. Setting these Boolean values will ensure that the page template design is displayed.

add_filter('wp_title','custom_error_title',65000,2);

The add_filter() function is used to replace the code of an existing function. In this case we are using our own function custom_error_title() to replace the title to be displayed in the browser’s title bar.

The 65000 here is the priority, lower ones are executed first. So why am I using such a high one here? Numerous WordPress SEO plugins use this filter to place custom titles. So using a high value ensures our code is the one that applies its function at the last thus replacing all changes. The fourth parameter 2 specifies the number of arguments required for function custom_error_title.

add_filter('body_class','custom_error_class');

Previously we modified the title, this filter is to modify the class attributes of <body>. By using our own function we are adding classes called “error403” and “error401” which can be used to provide a special CSS styling to this page if needed.

status_header(403);

This function sends a 403 Forbidden header (or 401 unauthorized). Without this a 200 OK will be sent which is highly inappropriate.

get_template_part('403');

This is were we include the 403.php file into the scene. Finally we stop WordPress from doing anything further.

exit;

Hope you got this working, do leave a comment on how helpful this was or if you have problem getting this work.

Related posts:

php category thumbnailRedirecting WordPress archives to a page php category thumbnailCreating a PHP MySQL Login Page Script windows category thumbnailSetting up an authoritative DNS in Windows Server 2008 jquery add remove textboxHow to Add/Remove text boxes using jQuery and PHP Default ThumbnailHow to create custom error page in Apache

Tags: custom error pages, php, wordpress

Comments

  1. geoff says

    December 18, 2013 at 6:25 am

    Thank you very much for the tutorial. I think this is something that WordPress should implement natively, as 403 and 401 errors aren’t completely unheard of. In the meanwhile, this is a very nice fix!

    Reply
  2. Mayeenul Islam says

    May 1, 2014 at 7:19 pm

    Thanks a lot for such a nice and clean attempt, and for the plugin too. Will help a lot of people out there for sure. All the very best.

    Reply
  3. Joanne says

    June 25, 2014 at 1:28 am

    Wow so nice of you to post the
    1) plugin option
    2) coding option

    Very complete and helpful tutorial – I’m all set with custom 404, 401, and 403 pages that I can manage without a plugin for now – and WITH a plugin in the future.

    Great post.

    Reply
  4. Jaromy says

    January 23, 2016 at 2:16 am

    Super helpful. Thanks so much for this tutorial and also the amazing plugin!

    Reply
  5. fabien says

    February 19, 2018 at 4:35 pm

    It seem this code doesn’t work because wordpress never set 403 code (always 404) so htaccess rules never match…

    Any Idea for a working code to do same think ?

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Get a wealth of information delivered to your inbox. Subscribe and never miss a single article.

  • Tutorials and howtos
  • Code, scripts and commands
  • Online Tools

* No spam, unsubscribe anytime

Hire Me

  • SSL installation and hardening (A+ on Qualys SSL test)
  • Apache & Nginx configuration
  • Email deliverability improvement (10/10 on Mail Tester & MailGenius)
  • WordPress customization, optimization and migration
  • and much more…

    Tools

    • DNS Lookup Tool
    • htdigest Generator Tool Online
    • htpasswd Generator Tool Online
    • HTTP Headers Lookup Tool
    • MD5 Encryption Tool
    • Open Port Check Tool
    • SHA-1 Encryption Tool
    • URL Encoding/Decoding Tool

    Nav

    • Home
    • About Me
    • Contact Me
    • Privacy Policy
    • Sitemap
    Vultr SSD VPS

    Creative Commons License
    Jesin's Blog by Jesin A is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
    Based on a work at websistent.com.