When configuring WordPress environments with Nginx, customizing error pages is often overlooked but can significantly enhance user experience. This article focuses specifically on implementing and optimizing custom error pages in Nginx for WordPress sites.
Common HTTP Status Codes in WordPress/Nginx Environments
In WordPress sites powered by Nginx, users typically encounter these error codes:
- 404 (Not Found): Missing pages, posts, or media
- 403 (Forbidden): Access restrictions to protected content
- 500 (Internal Server Error): PHP execution failures
- 502 (Bad Gateway): PHP-FPM communication issues
- 503 (Service Unavailable): Server overload or maintenance
- 504 (Gateway Timeout): PHP execution timeouts
Basic Nginx Error Page Configuration for WordPress
Add the following to your WordPress site’s Nginx server block:
server {
# Your existing WordPress configuration
# Define custom error pages
error_page 404 /custom_404.html;
error_page 500 502 503 504 /custom_50x.html;
# Error page locations
location = /custom_404.html {
root /var/www/html/custom-errors;
internal;
}
location = /custom_50x.html {
root /var/www/html/custom-errors;
internal;
}
}
The internal directive prevents direct access to error pages, improving security.
WordPress-Integrated Error Pages
Option 1: Static HTML with WordPress Styling
Create HTML files that match your WordPress theme’s design:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Page Not Found | Your WordPress Site</title>
<link rel="stylesheet" href="/wp-content/themes/your-theme/style.css">
</head>
<body class="error-page">
<header>
<div class="logo">
<a href="/"><img src="/wp-content/uploads/logo.png" alt="Site Logo"></a>
</div>
</header>
<main>
<section class="error-container">
<h1>404 - Page Not Found</h1>
<p>The content you're looking for doesn't exist or has been moved.</p>
<div class="error-actions">
<a href="/" class="btn">Return to Homepage</a>
<a href="/blog" class="btn">Browse Our Blog</a>
</div>
<div class="search-form">
<h3>Try searching:</h3>
<form action="/" method="get">
<input type="text" name="s" placeholder="Search...">
<button type="submit">Search</button>
</form>
</div>
</section>
</main>
<footer>
<p>© 2025 Your Company</p>
</footer>
</body>
</html>
Option 2: Dynamic PHP Error Pages with WordPress Integration
For a more powerful approach that leverages WordPress’s functionality:
1. Modify your Nginx configuration:
error_page 404 /custom_404.php;
location = /custom_404.php {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/custom-errors/custom_404.php;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_intercept_errors on;
}
2. Create a PHP error page that interfaces with WordPress:
<?php
// Initialize WordPress
define('WP_USE_THEMES', false);
require_once('/var/www/html/wp-load.php');
// Set the HTTP status code
status_header(404);
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<title>Page Not Found | <?php echo esc_html(get_bloginfo('name')); ?></title>
<?php wp_head(); ?>
<style>
.error-container {
text-align: center;
padding: 5% 0;
max-width: 800px;
margin: 0 auto;
}
.error-actions {
margin-top: 2rem;
}
.error-actions .btn {
display: inline-block;
padding: 10px 20px;
margin: 0 10px;
background: #0073aa;
color: white;
text-decoration: none;
border-radius: 4px;
}
</style>
</head>
<body <?php body_class('error-404'); ?>>
<?php get_header(); ?>
<div class="error-container">
<h1>404 - Page Not Found</h1>
<p>We couldn't find the page you were looking for.</p>
<div class="error-actions">
<a href="<?php echo esc_url(home_url('/')); ?>" class="btn">Return to Homepage</a>
</div>
<div class="search-form">
<h3>Try searching for what you need:</h3>
<?php get_search_form(); ?>
</div>
<?php
// Display recent posts
$recent_posts = wp_get_recent_posts(array(
'numberposts' => 3,
'post_status' => 'publish'
));
if ($recent_posts) {
echo '<div class="recent-posts">';
echo '<h3>Our latest articles:</h3>';
echo '<ul>';
foreach ($recent_posts as $post) {
echo '<li><a href="' . get_permalink($post['ID']) . '">' . $post['post_title'] . '</a></li>';
}
echo '</ul>';
echo '</div>';
}
?>
</div>
<?php get_footer(); ?>
<?php wp_footer(); ?>
</body>
</html>
This approach pulls in your WordPress theme’s header and footer while displaying relevant content like recent posts.
Context-Aware Error Pages in Nginx
Enhance user experience by providing context-relevant information on error pages:
Category-Based 404 Pages
location ~ ^/category/([^/]+)/ {
try_files $uri $uri/ /index.php?$args;
error_page 404 /custom_errors/category_404.php?category=$1;
}
location = /custom_errors/category_404.php {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/custom_errors/category_404.php;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
}
In category_404.php:
<?php
define('WP_USE_THEMES', false);
require_once('/var/www/html/wp-load.php');
$category_slug = isset($_GET['category']) ? sanitize_text_field($_GET['category']) : '';
$category = get_category_by_slug($category_slug);
status_header(404);
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<title>Content Not Found | <?php echo esc_html(get_bloginfo('name')); ?></title>
<?php wp_head(); ?>
</head>
<body <?php body_class('error-404'); ?>>
<?php get_header(); ?>
<div class="error-container">
<h1>404 - Content Not Found</h1>
<?php if ($category): ?>
<p>We couldn't find the specific content you were looking for in "<?php echo esc_html($category->name); ?>".</p>
<div class="category-content">
<h3>Popular posts in <?php echo esc_html($category->name); ?>:</h3>
<ul>
<?php
$category_posts = get_posts(array(
'category' => $category->term_id,
'numberposts' => 5,
'orderby' => 'comment_count'
));
foreach ($category_posts as $post) {
echo '<li><a href="' . get_permalink($post) . '">' . $post->post_title . '</a></li>';
}
?>
</ul>
</div>
<?php else: ?>
<p>We couldn't find the content you were looking for.</p>
<?php endif; ?>
</div>
<?php get_footer(); ?>
<?php wp_footer(); ?>
</body>
</html>
Implementing Maintenance Mode with 503 Status
Properly configured maintenance mode improves both user experience and SEO:
# At the top of your Nginx server block
if (-f $document_root/maintenance.html) {
return 503;
}
# Define the error page
error_page 503 /maintenance.html;
location = /maintenance.html {
root /var/www/html;
add_header Retry-After 3600; # Seconds until retry
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
Create a maintenance.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Scheduled Maintenance | Your WordPress Site</title>
<link rel="stylesheet" href="/wp-content/themes/your-theme/style.css">
<style>
.maintenance-container {
max-width: 800px;
margin: 100px auto;
text-align: center;
padding: 40px;
background: #f9f9f9;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.progress-bar {
height: 20px;
background: #eee;
border-radius: 10px;
margin: 30px 0;
overflow: hidden;
}
.progress {
height: 100%;
background: linear-gradient(90deg, #0073aa, #00a0d2);
width: 40%;
}
</style>
</head>
<body>
<div class="maintenance-container">
<img src="/wp-content/uploads/logo.png" alt="Site Logo" style="max-width: 200px;">
<h1>We're Upgrading Our Systems</h1>
<p>We're performing scheduled maintenance to improve your experience.</p>
<p>Expected completion: <strong>May 13, 2025 at 4:00 PM EST</strong></p>
<div class="progress-bar">
<div class="progress"></div>
</div>
<p>We apologize for any inconvenience.</p>
</div>
</body>
</html>
To activate maintenance mode, simply create the maintenance.html file, and remove it when maintenance is complete.
Rate Limiting with Custom Error Messages
Protect your WordPress site while providing helpful feedback:
# Define rate limiting zone
limit_req_zone $binary_remote_addr zone=wordpress_limit:10m rate=10r/s;
# Apply rate limiting to WordPress login
location = /wp-login.php {
limit_req zone=wordpress_limit burst=20 nodelay;
limit_req_status 429; # Too Many Requests
# Process PHP as normal
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
}
# Custom 429 error page
error_page 429 /custom_429.html;
location = /custom_429.html {
root /var/www/html/custom-errors;
internal;
}
Create custom_429.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rate Limited | Your WordPress Site</title>
<link rel="stylesheet" href="/wp-content/themes/your-theme/style.css">
</head>
<body class="error-page">
<div class="error-container">
<h1>Please slow down</h1>
<p>We've detected too many requests from your location.</p>
<p>Please wait a minute before trying again.</p>
<div class="error-actions">
<a href="/" class="btn">Return to Homepage</a>
</div>
</div>
</body>
</html>
Conclusion
Customizing Nginx error pages for WordPress sites is an important DevOps practice that enhances user experience while providing valuable diagnostic information. By implementing context-aware error handling, proper HTTP status codes, and informative content, you transform error pages from dead ends into useful navigation tools.
The technical approaches outlined in this article—from basic static error pages to dynamic WordPress integration—provide a scalable framework that DevOps engineers can implement across WordPress environments. These techniques not only improve user experience but also support SEO best practices by properly handling HTTP status codes and providing helpful alternatives to users.