skip to Main Content

Due to development needs, I want to display PHP errors loud and clear to the browser. So I set my php.ini:

display_errors = on

This works as expected.

But this messes up the Nginx status code if there is an error. As commented here https://www.php.net/manual/en/errorfunc.configuration.php#126734 :

It’s important to note that when display_errors is "on", PHP will send a HTTP 200 OK status code even when there is an error.

I already tried to set fastcgi_intercept_errors on (or off) as reccomended somewhere else, but it doesn’t seem to make any difference.

So, the question is: how can I have both display_errors = on and HTTP 500 on errors?

My relevant Nginx config is:

location ~ .php$ {
  try_files  $uri =404;
  include fastcgi_params;
  fastcgi_intercept_errors on;
  fastcgi_pass unix:/run/php/php-fpm.sock;
  fastcgi_param  SCRIPT_FILENAME  $request_filename;
}

Tests

I created a PHP file with a missing semicolon for testing. The PHP error is

Parse error: syntax error, unexpected end of file in test.php on line 2

PHP error with display_errors = on

Nginx returns 200 eve if there is an error:

192.168.0.1 - - [27/Sep/2022:08:50:49 +0200] "GET /test.php HTTP/2.0" 200 304 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"

PHP error with display_errors = off

Nginx returns 500:

192.168.0.1 - - [27/Sep/2022:08:53:52 +0200] "GET /test.php HTTP/2.0" 500 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"

4

Answers


  1. Have you tried to put:

    ini_set('display_errors', 1);
    ini_set('display_startup_errors', 1);
    error_reporting(E_ALL);
    

    on top of you`re php page?

    Login or Signup to reply.
  2. You should set the default status to 500.

    At the beginning of the code put http_response_code(500);

    At the end of the code put http_response_code(200);

    May be like this

    http_response_code( 500);
    ini_set('display_errors', 'on');
    http_response_code( 200);
    
    Login or Signup to reply.
  3. Here is a solution based on auto-prepend.
    You will need to set the auto_prepend_file directive:

    auto_prepend_file = "/path/to/prepend.php"
    

    The prepended script calls register_shutdown_function() to
    install a handler that will be executed when the main script ends. The handler checks whether an error has occurred;
    when it is the case, the HTTP status is set to 500.

    This technique is effective even for parse errors.

    Remark: to ensure that headers have not already been sent when the status is set, output buffering is enabled.

    Content of prepend.php:

    <?php
    ini_set('display_errors', 'On');
    ob_start();
    register_shutdown_function('shutdown');
    
    function shutdown()
    {
        $err = error_get_last();
        if($err && in_array($err['type'], [E_ERROR, E_PARSE, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR]))
            http_response_code(500);
    }
    ?>
    
    Login or Signup to reply.
  4. In fact, in the example you give, nothing surprises me :

    I created a PHP file with a missing semicolon for testing. The PHP
    error is Parse error: syntax error, unexpected end of file in test.php
    on line 2

    PHP error with display_errors = on Nginx returns 200 eve if there is
    an error

    The preprocessor fails to evaluate your php file because of the syntax error, nothing is done by your script (because it’s not a runtime error).

    With display_error = Off (default and production suggested value) in php.ini, PHP alerts via the protocol (500). With display_error = On it outputs something in the browser, so a 200 response is absolutely legitimate.

    You don’t explain why you want be able to generate both a protocol error and a regular error message, so I assume it could be to do a mix between a test and a production site. If I’m not wrong, the only way I know is to create a separate PHP pool on another port.

    It means :

    Keep display_errors = off default value in your php.ini

    Copy /etc/php/7.x/fpm/pool.d/www.conf with another name (test.conf for example)

    In this file, change the [www] name to [test]

    ; pool name ('www' here)
    [test]
    

    Change the listen directive with this name

    listen = /run/php/test.sock
    

    [Edit] Uncomment and modify the following line :

    php_flag[display_errors] = on
    

    Adapt your nginx.conf, creating a new server section, listening on another port :

    server {
      listen 8098;
      
      root   /path/to/root;
      index  index.html index.htm index.php;  
    
      location ~ .php$ {
        try_files  $uri =404;
        include fastcgi_params;
        fastcgi_intercept_errors on;
        fastcgi_pass unix:/run/php/test.sock;
        fastcgi_param  SCRIPT_FILENAME  $request_filename;
      }  
    }
    

    This way, the regular access to your website gives error 500, and the :8098 "super user" access shows the errors, as much as we can hope ^^

    (credits to french resource https://www.skyminds.net/php-configurer-un-pool-php-pour-chaque-site/)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search