12

I'm running PHP 5.4 on CentOS 7 and when there is a php file that throws an error (either an exception, or a syntax error) it returns an HTTP 200 status code instead of 500.

How can I get it to return a 500 server error when PHP encounters an error?

I've tried looking at other Stackoverflow posts, and they all seem to point to solutions around returning your own 500 error code (which I believe should be the normal behavior of PHP on it's own without me needing to manually fire the http header, as per this info: PHP emitting 500 on errors - where is this documented?) It also points to Xdebug as being a possible issue, but my problem persists even when I rebuild the server without Xdebug.

The relevant settings I have are:

  • display_errors: on
  • display_startup_errors: on
  • error_reporting: -1 (this causes all errors to be reported)
Community
  • 1
  • 1
Dan Bowling
  • 1,205
  • 1
  • 15
  • 41
  • 1
    why would you want to? Apache errors and php errors are not the same thing –  Mar 10 '15 at 19:59
  • But he'll probably handle any PHP-side exception in a way that PHP responds with `500` automatically. – try-catch-finally Mar 10 '15 at 20:36
  • @Dagon I'm talking about http status codes served by apache. I'm trying to get the behavior where a PHP error triggers the 500 status code. – Dan Bowling Mar 10 '15 at 20:49
  • yes, but why? seems like a terrible idea –  Mar 10 '15 at 20:50
  • 3
    @Dagon Could you explain why you think this is terrible? 500 status codes mean something, just like 200 does. A broken page returning a 200 doesn't help much in the debugging process, and we'd also not want search engines to index the error page text while an app is temporarily erroring out. – Dan Bowling Mar 10 '15 at 20:53
  • how can saying it was an Apache error when it was really a php error do anything other than making debugging harder. There's a reason you couldn't find any other posts on it - no one in the universe thinks it s a good idea. –  Mar 10 '15 at 20:57
  • @dagon well, thanks for your help. I've found an official PHP statement on the issue, so I'll post that as an answer in a moment. It doesn't address the philosophical issue you're trying to raise, but it does point at the way the behavior works and why. – Dan Bowling Mar 10 '15 at 21:08
  • I realize this is old, and http status falls outside my expertise, but I felt the need to finish a PhD in philosophy, so I might as well take my one opportunity. Saying that two isolated errors don't denote a single, contextually indistinguishable failure is both logically and linguistically indefensible. You simply don't have the information necessary to ask any questions about the potentially erroneous state, let alone have an answer on it. It, OF COURSE, represents whatever OP considers a total failure - a question never once asked, so please, if you want to help, be helpful :) – MJHd Jan 27 '20 at 15:46

4 Answers4

18

According to a PHP bug report, the behavior described here is due to how display_errors is set.

[2010-02-03 19:03 UTC] derick@php.net

The reason why display errors needs to be turned of, is because displayed errors generate output, and output causes the headers to be send out. I'm afraid we can't do much about this.

So the answer to the question is that:

  • When display_errors is on it will return 200 always.
  • When display_errors is off it will return 500.
Dan Bowling
  • 1,205
  • 1
  • 15
  • 41
  • 3
    Somebody's posted a pull request on that bug just last month. Maybe we need to badger the php dev community to get it approved. – Jeff Jun 11 '18 at 00:14
3

A work around to bug #50921: use auto_prepend_file to set the http response code to 500, then set it to 200 when you know the page has bootstrapped. A representative, but not comprehensive, example:

# php.ini
auto_prepend_file = /path/to/auto_prepend_file.php

# auto_prepend_file.php
<?php http_response_code(500);

# index.php
<?php
register_shutdown_function(function () {
    $error = error_get_last();
    switch ($error['type'] ?? 0) {
    case E_ERROR:                                                         
    case E_PARSE:                                                         
    case E_CORE_ERROR:                                                    
    case E_COMPILE_ERROR:                                                 
    case E_RECOVERABLE_ERROR:                                             
        file_put_contents('/path/to/log', $error['message'], FILE_APPEND);
        http_response_code(500);
        echo 'Something went horribly wrong.';
        echo str_repeat(' ', 512);
        break;
});

http_response_code(200);
require_once '/path/to/bootstrap.php';

If index.php has a crashing error in it, the 500 set via auto-prepend file will still execute. There won't be any output unless display_error and/or display_startup_error are on, though. If bootstrap.php or any of its dependents have crashing error, then the shutdown function will handle those.

There are still scenarios here, which need to be considered:

  • What format should be output? Check ACCEPT header, or default accordingly.
  • What if output has already been started? How do you interrupt the stream to signal something went sideways?

A lot of this depends upon how much control you have over your environment (eg, write to INI_SYSTEM level config) and how your framework operates (eg ob_start, or no).

I'll emphasize this is just a workaround. If your auto_prepend_file itself has parse errors, then you're back to square one: PHP emits a 200 status error code. Fixing the engine to emit a 500 is the only proper way to break this chicken-and-egg cycle.

bishop
  • 37,830
  • 11
  • 104
  • 139
2

Note that this may sometimes be down to the CustomLog format in apache. For example %>s ("the final status") can (correctly) yield a 403 in the access log whereas just %s may record a 200 even when the status code of the response was something else.

mcdruid
  • 21
  • 1
1

I found the solution to be even simpler than any of the other answers on the topic.

This solution works whether you have display_errors enabled or not. Simply set the response code to 500 at the very beginning, so that any output will trigger that header to be output. Then at the very end of your file before you send the normal response output, set the response code to 200.

http_response_code() is used to set the response code when the headers are output. It does not cause any header to be output immediately, so you can change it as much as you want before generating output, and only the last status set before output sticks. You could even change it temporarily to some other code than 500 if you know a crash in a certain part of your code implies a different status result.

// Top of index file...
http_response_code( 500);   // Internal Server Error on any error output
error_reporting(E_ALL & ~E_CORE_WARNING & ~E_STRICT);
ini_set('display_errors', 'on');

.
.
.

// End of file, success!

http_response_code( 200);   // OK
echo json_encode($reply_data);

I find it very handy to be able to have errors displayed during development, but still have the proper HTTP status returned.

Agent Friday
  • 169
  • 2
  • 12