Make WordPress Core

Opened 4 weeks ago

Last modified 10 days ago

#64256 new enhancement

Add HTTP 500 status code for WordPress critical error messages

Reported by: swissky's profile swissky Owned by:
Milestone: 7.0 Priority: normal
Severity: normal Version:
Component: Bootstrap/Load Keywords: has-patch changes-requested
Focuses: Cc:

Description

Summary

Ensure HTTP 500 status code is always returned for WordPress critical error messages

Component

Bootstrap/Load (or Site Health)

Description

Currently, when WordPress displays the critical error message "There has been a critical error on this website.", it does not consistently return an HTTP status code. While the documentation mentions that fatal errors "typically" return HTTP 500, there is no guarantee that monitoring systems can reliably detect these errors.

Problem

Monitoring tools cannot reliably detect WordPress critical errors because:

  • No HTTP status code is guaranteed to be returned
  • The response may appear as HTTP 200 (success) to monitoring systems
  • Critical errors go undetected until manual inspection
  • Existing documentation mentions "typically 500" but doesn't guarantee monitoring-friendly behavior

Related Tickets

Related to ticket #44458 (WSOD - White Screen of Death) which addresses error handling improvements.

Proposed Solution

Ensure that WordPress always sets HTTP status code 500 when displaying critical error messages, regardless of whether a custom php-error.php template exists or not. This ensures:

  • Monitoring systems can automatically detect critical errors
  • HTTP status codes correctly reflect the error state
  • Standard HTTP error handling works as expected
  • Consistent behavior across all WordPress installations

Implementation Options

Option 1: Always set HTTP 500

Modify _default_wp_die_handler() in wp-includes/functions.php to always set HTTP status code 500 when displaying critical error messages, even if headers were already sent.

Option 2: Filter/Feature Flag

Add an optional filter force_500_on_fatal_error that allows forcing HTTP status code 500 for fatal errors, ensuring backward compatibility while enabling monitoring-friendly behavior.

Option 3: Ensure php-error.php sets status code

Document and ensure that custom php-error.php templates should set HTTP status codes, and provide a default implementation that always sets 500.

Current Behavior

  • class-wp-fatal-error-handler.php sets 'response' => 500 in args (line 212)
  • _default_wp_die_handler() calls status_header($parsed_args['response']) (line 3884)
  • However, this only works if !did_action('admin_head') and headers haven't been sent
  • Custom php-error.php templates may not set status codes

Testing

Tested scenarios:

  • cURL to verify HTTP status code: curl -I https://example.com/test-fatal-error.php
  • Browser developer tools to check response headers
  • Monitoring systems to verify error detection
  • Cases where headers were already sent

Backward Compatibility

This change should be backward compatible:

  • Only affects HTTP status codes, not error messages
  • Doesn't change existing error handling logic
  • Can be implemented as opt-in via filter if needed

Use Case

Monitoring systems rely on HTTP status codes to detect server errors. Currently, WordPress critical errors may not return HTTP 500, causing:

  • False negatives in monitoring alerts
  • Delayed detection of critical issues
  • Inconsistent error reporting

References

  • WordPress documentation mentions fatal errors "typically" return HTTP 500
  • Related to ticket #44458 (WSOD improvements)
  • Monitoring systems require reliable HTTP status codes for error detection

Attachments (2)

wp-includes-functions.php.patch (1.2 KB) - added by swissky 4 weeks ago.
64256.patch (1.3 KB) - added by swissky 4 weeks ago.
Patch Update

Download all attachments as: .zip

Change History (8)

#1 @SergeyBiryukov
4 weeks ago

Hi there, welcome back to WordPress Trac! Thanks for the ticket.

Looking at the patch, checking if the message contains either critical error or There has been a critical error seems redundant, as the first one should suffice. But more importantly, how would this work for messages translated into other languages?

#2 @swissky
4 weeks ago

Thank you for the feedback! You're absolutely right on both points. I've updated the patch to address these issues.

Changes Made

  1. Removed Redundant String Checks: The patch now uses a single, language-independent method instead of checking multiple strings.
  1. Language-Independent Solution: Instead of string matching (which fails with translations), the patch now:
  • Checks for error code 'internal_server_error' - This is the code used by WordPress fatal error handler (see class-wp-fatal-error-handler.php line 238)
  • Also checks $parsed_args['code'] - In case the code is set there instead
  • Ensures HTTP 500 if response is already 500 - Covers cases where the fatal error handler already set it

Why This Works

  1. Language-independent: Error code 'internal_server_error' is always the same, regardless of language
  2. Uses WordPress internals: The fatal error handler creates WP_Error with this exact code
  3. Covers all cases: Checks both WP_Error code and parsed_args code
  4. No redundancy: Single, clear check

The updated patch is attached.

@swissky
4 weeks ago

Patch Update

#4 @westonruter
3 weeks ago

  • Keywords has-patch changes-requested added
  • Milestone changed from Awaiting Review to 7.0

This makes sense to me.

Using this sample plugin code, for example:

<?php
add_action( 'init', function () {
        non_existing_function();
} );

I get a 200 OK response, unexpectedly, even though PHP is dumping out:

Fatal error: Uncaught Error: Call to undefined function non_existing_function()

@swissky Can you open a pull request to facilitate collaboration and automated checks?

#6 @whiteshadow01
10 days ago

Since the author hasn't replied in 2 weeks, I have opened a PR on github as requested by @westonruter

Note: See TracTickets for help on using tickets.