PHP hides errors by default on most server installs, because showing them to visitors leaks information. The result for a developer is the white screen: something failed, and nothing says what. Two settings control it, display_errors and error_reporting, and there is always a log somewhere even when the screen is blank. This article covers turning errors on where you should, finding the log where you should not, and the handful of cases where errors are hidden even with everything on.
Quick fix during development#
At the very top of the script, before anything else:
<?php
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
Reload. Notices, warnings and most fatal errors now print with a file and line number. This is the fastest way to find out what is wrong, and it has one limit covered below: it cannot show errors that happen before that line runs.
The two settings#
error_reporting decides which errors count. E_ALL is everything. Older tutorials suggest E_ALL & ~E_NOTICE to hide notices; do not, because notices are often the first sign of a real bug.
display_errors decides whether counted errors are printed in the output. On, they appear on the page. Off, they go only to the log.
A third, log_errors, decides whether they are written to the log at all, and error_log says where. In production: display off, log on.
Where to set them permanently#
php.ini#
The proper place. Find the file the web server is using — not necessarily the one the command line uses:
<?php phpinfo(); // look for "Loaded Configuration File"
php --ini # the CLI's file, which is often a different one
Edit the loaded file:
display_errors = On ; development only
display_startup_errors = On
error_reporting = E_ALL
log_errors = On
error_log = /var/log/php_errors.log
Restart Apache or PHP-FPM afterwards; php.ini is read at start-up.
.htaccess (Apache with mod_php)#
php_flag display_errors on
php_value error_reporting -1
Works only when PHP runs as an Apache module. Under PHP-FPM, these lines cause a 500 error — which is itself a common reason for a page that shows nothing. Remove them and use php.ini or a .user.ini file instead.
.user.ini (PHP-FPM and shared hosting)#
display_errors = On
error_reporting = E_ALL
Placed in the site’s root, picked up within a few minutes without a restart. This is usually the only option on shared hosting.
Errors that happen before your code runs#
A parse error — a missing semicolon, an unclosed bracket — stops PHP before it executes anything, including your ini_set line. The screen stays blank even with the quick fix in place. Two ways round it:
php -l broken.php # lint: reports syntax errors without running the file
Or put the settings in php.ini or .user.ini, which apply before parsing. The same applies to fatal errors inside a file that is included before the settings line; move the settings to the very first file that runs.
Finding the log#
When display is off, the error is still recorded. Where depends on the setup:
| Setup | Log location |
|---|---|
| XAMPP on Windows | C:\xampp\php\logs\php_error_log and C:\xampp\apache\logs\error.log |
| XAMPP on macOS | /Applications/XAMPP/logs/php_error_log |
| Ubuntu, Apache mod_php | /var/log/apache2/error.log |
| Ubuntu, PHP-FPM | /var/log/php8.3-fpm.log and the site’s Apache or nginx error log |
| Docker official image | Container stderr: docker logs container-name |
| Shared hosting | An error_log file next to the script, or the control panel’s log viewer |
tail -f /var/log/apache2/error.log # watch it live while you reload the page
If nothing appears anywhere, log_errors is off or error_log points somewhere unwritable. Set both explicitly in php.ini.
Frameworks#
Laravel, Symfony and WordPress each layer their own handling on top:
- Laravel:
APP_DEBUG=truein.envshows the full error page; logs are instorage/logs/laravel.log. - Symfony:
APP_ENV=devenables the debug toolbar and detailed errors; logs invar/log. - WordPress:
define('WP_DEBUG', true);anddefine('WP_DEBUG_LOG', true);inwp-config.phpwrite towp-content/debug.log.
With a framework, use its switch rather than ini_set; the framework may override the raw settings.
Production#
Display off, log on, and read the log. A good production setup:
display_errors = Off
log_errors = On
error_reporting = E_ALL
error_log = /var/log/php/app.log
Combine it with a custom error handler or a framework’s logging so that errors reach somewhere you actually look — a monitoring service, or at least an email. A log nobody reads is not much better than no log.
Questions people ask#
Why do I see a 500 error instead of the PHP error?
A fatal error with display off produces a bare 500 from the server. The detail is in the PHP or web-server log. A 500 caused by a bad .htaccess line is in the Apache log only.
Errors show on one site and not another on the same server.
A .user.ini, .htaccess or framework setting in the second site is overriding php.ini. Check the site’s root and its config.
Does error_reporting(E_ALL) slow the site down?
No. Reporting affects what is recorded, not how the code runs. Displaying and logging thousands of notices does have a cost, which is a reason to fix the notices.
What is the difference between a warning and a notice?
Both let execution continue. A warning signals something likely wrong, such as a missing file; a notice signals something sloppy, such as an undefined variable. Since PHP 8, many former notices are warnings, and some warnings are now exceptions.
Where to go next#
- Handling errors and exceptions in PHP — catching them in code.
- PHP code showing as plain text — when PHP did not run at all.
- Logging an array in PHP — getting useful data into the log.