How to Enable PHP Error Reporting and Display Errors Correctly

A flat geometric code file icon in violet-blue featuring a gold warning triangle. Text: ENABLE PHP ERRORS.

Your PHP script throws an error and the browser shows nothing but a blank white page. That “White Screen of Death” is PHP hiding the error, not the absence of one, and three settings decide whether you ever see it.

The Quick Fix (Runtime) 

If you’re staring at a blank screen right now, paste these three lines at the very top of your PHP file, immediately after the opening <?php tag:

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

Refresh the page. If the problem is a runtime error (undefined variable, failed database call, type mismatch), you’ll see the full error message in the browser instead of a blank screen.

Each line controls a different behavior:

  • display_errors tells PHP to print errors to the browser instead of hiding them. It should never be on in production because error output can expose file paths and database credentials.
  • display_startup_errors surfaces errors from PHP’s startup phase (a misconfigured extension or bad php.ini directive). Set in php.ini it works as described; set at runtime with ini_set() it is too late to affect the current request’s startup, so keep it here for completeness but rely on the php.ini version for startup problems.
  • error_reporting(E_ALL) sets the reporting level. E_ALL includes every category: fatal errors, warnings, notices, and deprecation warnings. (Through PHP 8.3 it also included E_STRICT, which was removed from E_ALL in 8.4.)
ini_set('display_errors', 1) only reveals errors that happen AFTER this line runs, so it cannot show parse errors, which fail at compile time before any of your code executes. If your script has a parse error (missing semicolon, unmatched bracket), PHP crashes before ini_set() ever runs. For parse errors, enable display_errors in php.ini or .htaccess, or check syntax from the command line with php -l yourfile.php.

These three settings are independent concepts that work together:

  • error_reporting decides which error types PHP tracks internally (the filter).
  • display_errors decides where tracked errors go: the browser (On) or nowhere visible (Off).
  • log_errors decides whether tracked errors are written to a log file, independent of whether they’re displayed.

Setting error_reporting(E_ALL) without display_errors = On means PHP tracks all errors but doesn’t show them. Setting display_errors = On without a broad error_reporting level means PHP shows errors, but only the categories you’ve included.

Configure php.ini for Development vs Production 

The paths and service names below use Debian and Ubuntu conventions (/etc/php/<version>/..., apache2, php<version>-fpm). On RHEL-family systems (Alma, Rocky, Fedora) the config lives under /etc/php.ini and /etc/php-fpm.d/, and the services are httpd and php-fpm; the directives themselves are identical.

The runtime approach works for one-off debugging, but if you manage your own server, configuring php.ini globally is more reliable. It catches parse errors and applies to every script without code changes. If you’re not sure where your php.ini lives, see our guide on finding and editing php.ini on Linux and Windows.

Development Settings 

On a development machine, you want maximum visibility. Open your php.ini and set:

php.ini
display_errors = On
display_startup_errors = On
error_reporting = E_ALL
log_errors = On
error_log = /var/log/php_errors.log

Save and exit (press CTRL + X, press Y, then press ENTER)

This combination shows errors in the browser and writes them to the log file. Having both active during development means you can see errors immediately and still search the log for issues that happened earlier in the session.

PHP 8.0 changed the default error_reporting level to E_ALL. Prior versions defaulted to E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED, which hid notices and deprecation warnings. If you recently upgraded to PHP 8.0+, you may see new warnings in code that ran cleanly before.

Production Settings 

On a live server, displaying errors to visitors is a security risk. Error messages can expose internal file paths, database hostnames, and even credentials in stack traces. The PHP manual is explicit: “display_errors should always be disabled in a production environment, as it can include confidential information such as database passwords.”

php.ini
display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL
log_errors = On
error_log = /var/log/php_errors.log

Save and exit (press CTRL + X, press Y, then press ENTER)

Make sure the error_log path is writable by the user your web server or PHP-FPM runs as (often www-data). If it is not, PHP silently drops the log lines and you are left debugging a log file that never fills. Create it and hand it to the user PHP runs as (www-data on Debian/Ubuntu, often apache or nginx elsewhere): sudo touch /var/log/php_errors.log && sudo chown www-data /var/log/php_errors.log.

Notice that error_reporting stays at E_ALL even in production. You still want PHP to track every error category internally. The difference is that display_errors = Off prevents them from reaching the browser, while log_errors = On routes them to a file only you can read.

After changing php.ini, restart the PHP process for the new values to take effect. On Apache with mod_php:

Terminal
sudo systemctl restart apache2

On Nginx with PHP-FPM (replace 8.3 with your version):

Terminal
sudo systemctl restart php8.3-fpm

Verify the active settings by creating a temporary phpinfo() page and viewing it in a browser (delete it as soon as you are done, see the security note in the verification section below). Note that php -i on the command line checks the CLI config (/etc/php/8.3/cli/php.ini), which is a different file from the web SAPI. Apache mod_php uses /etc/php/8.3/apache2/php.ini, and PHP-FPM uses /etc/php/8.3/fpm/php.ini. Always verify through the same SAPI your site uses.

Enable Errors via .htaccess (Apache Only) 

On shared hosting where you can’t edit php.ini, Apache’s .htaccess file can override PHP directives at the directory level. This only works when PHP runs as an Apache module (mod_php), not under PHP-FPM.

Add these lines to the .htaccess file in your site’s document root:

.htaccess
php_flag display_errors On
php_flag display_startup_errors On
php_value error_reporting -1

The value -1 for error_reporting is equivalent to E_ALL but is future-proof: it enables every possible error level, including any new ones added in future PHP versions. Use php_flag for boolean directives (On/Off) and php_value for directives that take a numeric or string value.

If you see an Internal Server Error (HTTP 500) after adding these lines, your hosting provider may have disabled .htaccess PHP overrides, or PHP is running under FPM instead of mod_php. Check with your host or use the runtime ini_set() method instead.

PHP-FPM Error Logging (Nginx) 

If you run Nginx with PHP-FPM, there’s an extra layer that trips up many developers. With display_errors = On, errors still reach the browser under PHP-FPM. The gap is the log: PHP-FPM’s worker processes discard their stderr by default, so anything a worker writes to stderr (startup output, fatal errors that bypass PHP’s handler) is lost unless you explicitly tell FPM to capture it. PHP’s own error_log writes are unaffected; this is about the stderr stream on top of them.

Open your PHP-FPM pool configuration file. If you’re not sure where it is, see our guide to finding your PHP-FPM config file. The default pool is usually at /etc/php/8.3/fpm/pool.d/www.conf (adjust the version number for your install).

Find or add the catch_workers_output directive:

/etc/php/8.3/fpm/pool.d/www.conf
; # DevAnswers.net, redirect worker output to the main error log
catch_workers_output = yes
decorate_workers_output = no

Save and exit (press CTRL + X, press Y, then press ENTER)

Here’s what each directive does:

  • catch_workers_output = yes redirects the worker’s stdout and stderr into the main FPM error log. Without this, both streams go to /dev/null per the FastCGI specification, meaning your errors vanish completely.
  • decorate_workers_output = no (available since PHP 7.3) prevents FPM from wrapping every error line with timestamps and pool metadata. With decoration on, a simple “Undefined variable” notice becomes a multi-line block that’s harder to parse. Turning it off keeps the log clean.

After saving, restart PHP-FPM for the changes to take effect:

Terminal
sudo systemctl restart php8.3-fpm

With catch_workers_output on, whatever the PHP worker writes to stderr, startup errors and fatal errors that bypass PHP’s own handler, is captured into the FPM error log (typically /var/log/php8.3-fpm.log). That is separate from the error_log file you set in php.ini: PHP writes its normal errors straight to that file, while catch_workers_output catches the worker output that would otherwise vanish, so under FPM you may have two logs to check. If display_errors is on, errors also reach the browser. To find your Nginx-specific error log location, see our guide on finding the PHP error log on Nginx.

You can set catch_workers_output per pool. If you have separate FPM pools for staging and production, enable it only on the staging pool to keep production logs leaner.

PHP Error Levels Reference 

PHP defines error levels as bitmask constants. You can combine them with the bitwise OR (|) operator to report specific categories, or exclude individual levels with AND-NOT (& ~). The table below covers the most common levels; see the PHP manual for the full list.

ConstantValueDescriptionFatal?
E_ERROR1Fatal runtime error. Script execution halts.Yes
E_WARNING2Runtime warning. Execution continues.No
E_PARSE4Compile-time parse error (syntax mistakes).Yes
E_NOTICE8Runtime notice for minor issues. (Note: accessing an undefined variable or array key became an E_WARNING in PHP 8.0, not a notice.)No
E_CORE_ERROR16Fatal error during PHP startup.Yes
E_CORE_WARNING32Non-fatal warning during PHP startup.No
E_COMPILE_ERROR64Fatal compile-time error (from Zend Engine).Yes
E_COMPILE_WARNING128Non-fatal compile-time warning.No
E_USER_ERROR256User-generated fatal error via trigger_error().Yes
E_USER_WARNING512User-generated warning.No
E_USER_NOTICE1024User-generated notice.No
E_STRICT2048Suggestions for forward compatibility. Was part of E_ALL from PHP 5.4 through 8.3. Deprecated as of PHP 8.4: it was removed from E_ALL, referencing the constant now emits a deprecation notice, and it is scheduled for removal in PHP 9.0.No
E_RECOVERABLE_ERROR4096Catchable fatal error. Can be handled by a custom error handler.Depends
E_DEPRECATED8192Runtime deprecation warning. Code works now but will break in a future version.No
E_USER_DEPRECATED16384User-generated deprecation via trigger_error().No
E_ALLvariesEvery error level. The integer value changes between PHP versions as levels are added or removed (E_STRICT left E_ALL in 8.4), so use the E_ALL constant rather than a hard-coded number.N/A

One modern caveat: E_STRICT is deprecated as of PHP 8.4. It has been folded into other categories over the years, and on PHP 8.4+ using the constant itself raises a deprecation notice (with removal planned for PHP 9.0). On current PHP you no longer need to reference E_STRICT separately; E_ALL already reports what it used to cover.

Common combinations you’ll see in the wild:

PHP
// Development: show everything
error_reporting(E_ALL);

// Legacy apps: hide notices and deprecation warnings
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);

// Strict: only fatal errors and warnings
error_reporting(E_ERROR | E_WARNING | E_PARSE);

// Future-proof: every possible level, including future additions
error_reporting(-1);

The -1 value deserves special mention. It enables every possible error level, including any new constants added in future PHP versions. In practice, E_ALL and -1 produce the same result today, but -1 is the safer choice for long-lived configuration files.

Conclusion 

For quick debugging, ini_set() and error_reporting(E_ALL) at the top of your script will get errors on screen immediately. For anything beyond a one-off fix, configure php.ini properly: display_errors = On in development, display_errors = Off with log_errors = On in production. If you run Nginx, don’t forget catch_workers_output = yes in your FPM pool config.

Support DevAnswers
Written, tested and maintained by one developer. Your support keeps these guides accurate and ad-light.
Buy me a coffee

1 reply

Leave a reply

  1. Roman

    Thank you!