Secure Error Handling in WordPress
Learn how to handle errors securely in WordPress without exposing sensitive information. Configure debug settings and implement safe error logging.
Error handling in WordPress requires balancing debugging needs with security. Improperly configured error display can leak sensitive information including file paths, database credentials, and server configuration.
The Risk of Error Exposure
Information Leaked in Errors
- Full server file paths
- Database table names and structure
- PHP version and extensions
- Plugin and theme file locations
- SQL queries and parameters
- Internal function names
How Attackers Use This Information
Exposed errors help attackers:
- Map server directory structure
- Identify vulnerable components
- Craft targeted SQL injection
- Discover hidden functionality
WordPress Debug Configuration
Production Settings
// wp-config.php - Production
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);
@ini_set('display_errors', 0);
@ini_set('log_errors', 1);
Development Settings
// wp-config.php - Development only
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', true);
define('SCRIPT_DEBUG', true);
define('SAVEQUERIES', true);
Secure Error Logging
Custom Log Location
// Move debug log outside web root
define('WP_DEBUG_LOG', '/var/log/wordpress/debug.log');
// Ensure directory exists and is writable
// chmod 750 /var/log/wordpress
// chown www-data:www-data /var/log/wordpress
Log Rotation
// Add to cron for log rotation
0 0 * * * /usr/sbin/logrotate /etc/logrotate.d/wordpress
Custom Error Handlers
Graceful Error Pages
// Custom error handler
set_error_handler(function($errno, $errstr, $errfile, $errline) {
// Log the actual error
error_log(sprintf(
"Error [%d]: %s in %s on line %d",
$errno, $errstr, $errfile, $errline
));
// Show generic message in production
if (!WP_DEBUG) {
return true; // Suppress the error display
}
return false; // Let WordPress handle in debug mode
});
Exception Handler
// Custom exception handler
set_exception_handler(function($exception) {
// Log full exception details
error_log(sprintf(
"Exception: %s in %s:%d
Stack trace:
%s",
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
$exception->getTraceAsString()
));
// Show user-friendly error
if (!WP_DEBUG) {
wp_die(
'An unexpected error occurred. Please try again later.',
'Error',
array('response' => 500)
);
}
throw $exception;
});
Database Error Security
Suppress Database Errors
// Hide database errors in production
global $wpdb;
if (!WP_DEBUG) {
$wpdb->suppress_errors(true);
$wpdb->hide_errors();
}
Safe Query Error Handling
// Handle query errors gracefully
function safe_query($sql, $params = []) {
global $wpdb;
$wpdb->suppress_errors(true);
$result = $wpdb->get_results($wpdb->prepare($sql, $params));
if ($wpdb->last_error) {
error_log('Database error: ' . $wpdb->last_error);
return false;
}
return $result;
}
AJAX Error Handling
Secure AJAX Responses
// Safe AJAX error response
function handle_ajax_request() {
try {
// Process request
$result = process_data();
wp_send_json_success($result);
} catch (Exception $e) {
// Log the real error
error_log('AJAX Error: ' . $e->getMessage());
// Send generic response
wp_send_json_error(array(
'message' => 'An error occurred. Please try again.'
), 500);
}
}
REST API Error Security
Custom REST Error Response
// Filter REST API errors
add_filter('rest_request_after_callbacks', function($response, $handler, $request) {
if (is_wp_error($response) && !WP_DEBUG) {
// Log actual error
error_log('REST Error: ' . $response->get_error_message());
// Return sanitized error
return new WP_Error(
'request_failed',
'Request could not be processed',
array('status' => 500)
);
}
return $response;
}, 10, 3);
Fatal Error Handling
Recovery Mode
WordPress 5.2+ includes fatal error recovery:
// Disable recovery mode emails to admin
add_filter('recovery_mode_email', function($email) {
// Customize or disable recovery emails
return $email;
});
Custom Fatal Error Page
// Register shutdown function
register_shutdown_function(function() {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR])) {
error_log(sprintf(
"Fatal Error: %s in %s on line %d",
$error['message'],
$error['file'],
$error['line']
));
if (!WP_DEBUG && !headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
include ABSPATH . '/wp-content/themes/mytheme/500.php';
exit;
}
}
});
PHP Configuration
# php.ini production settings
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/error.log
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
Monitoring Errors
- Set up log monitoring alerts
- Track error frequency patterns
- Review logs regularly for issues
- Use centralized logging for multiple sites
Conclusion
Secure error handling hides sensitive information from attackers while maintaining debugging capability. Configure production sites to log errors privately and display generic messages to users.
Written by Sarah Chen
WP Folder Shield Team