Question
Why are PHP warnings and errors written to the Apache error log instead of being displayed in the browser?
In this script, $err is created only inside the POST branch but is later used in foreach ($err as $line). This should produce an undefined-variable warning on an initial GET request. Also, changing an SQL statement from INSERT INTO to the invalid DELETE INTO should cause a PDO error.
<?php
error_reporting(E_ALL);
?>
<!-- Later in the page -->
<?php foreach ($err as $line) { ?>
<div><?php echo $line; ?></div>
<?php } ?>
Even after setting display_errors = On and error_reporting = E_ALL | E_STRICT in php.ini and restarting Apache, no errors or warnings appear on the page. What PHP settings and code flow issues control whether errors are displayed in the browser?
Short Answer
PHP has separate controls for which errors are reported and where they are sent. error_reporting() selects error levels, while display_errors determines whether reported errors are printed in the HTTP response. Errors can still be logged when browser display is disabled. You will also learn why a redirect can make an error message appear to vanish and how to initialize variables so ordinary requests do not create warnings.
Concept
PHP error handling has two related but separate configuration decisions:
error_reporting: Which categories of PHP errors PHP should report.display_errors: Whether PHP should add reported errors to the response sent to the browser.log_errors: Whether PHP should write reported errors to a server log.
For local development, a common setup is:
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
For production, displaying errors is unsafe because messages can reveal file paths, database details, configuration, or application logic. Production applications normally log errors but show a safe, generic error page.
In the provided code, error_reporting(E_ALL) enables reporting, but it does not force browser display. If display_errors is disabled for the PHP web-server environment, the warning goes only to the log.
There is also a control-flow issue: after a PDO exception is caught and echoed, the script still runs:
header('Location: ' . $_SERVER[]);
;
Mental Model
Think of PHP errors as alerts from a building alarm system:
error_reportingdecides which sensors are active.display_errorsdecides whether the alert appears on the public display screen (the browser).log_errorsdecides whether the alert is written in the security logbook (the server log).
An alert can be recorded in the logbook without being shown publicly.
A redirect is like immediately sending the visitor to a different room. Even if an alert briefly appeared in the first room, the visitor ends up looking at the second room instead.
Syntax and Examples
Enable detailed errors temporarily during local development:
<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
Place this at the beginning of the executed PHP file, before output. This is useful for runtime errors in that file.
For errors that happen before the script can run, such as PHP startup or syntax errors, configure the web-server PHP configuration instead:
display_errors = On
display_startup_errors = On
error_reporting = E_ALL
log_errors = On
The option name is exactly display_errors. A misspelling such as display_erros is ignored because it is not a valid PHP directive.
Initialize variables before branches that use them:
<?php
$err = [];
$form = [
'display_name' => '',
'email' => '',
'password' => ,
];
([] === ) {
(([] ?? )) {
[] = ;
}
}
( ) {
. (, ENT_QUOTES, ) . ;
}
Step by Step Execution
Consider this small example:
<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = trim($_POST['email'] ?? '');
if ($email === '') {
$errors[] = 'Email is required.';
}
}
foreach ($errors as $error) {
echo $error . PHP_EOL;
}
On an initial page visit:
- PHP enables reporting and browser display for this request.
$errorsbecomes an empty array.- The request method is usually
GET, so thePOSTblock is skipped. foreachruns over an empty array.- Nothing is printed, and no warning occurs.
When the form is submitted with an empty email:
Real World Use Cases
- Local debugging: Show warnings, notices, and stack traces while building a feature on a local machine.
- Production monitoring: Keep
display_errorsdisabled but use logs to investigate failures without exposing internal details to visitors. - Form validation: Create an errors array before validating fields so every request can safely render error messages.
- Database operations: Catch database exceptions, log technical details, and show users a general failure message.
- API development: Return controlled JSON error responses rather than raw PHP warnings that would break the API response format.
- Deployment troubleshooting: Compare the PHP configuration used by the web server with the configuration used by the command line.
Real Codebase Usage
Real projects usually separate developer diagnostics from user-facing responses.
Validate first, then perform database work
$errors = [];
$email = trim($_POST['email'] ?? '');
if ($email === '') {
$errors[] = 'Email is required.';
}
if ($errors !== []) {
// Render the form again with errors.
return;
}
// Only attempt the database operation after validation succeeds.
Catch, log, and return a safe message
try {
$statement = $pdo->prepare(
'INSERT INTO users (display_name, email) VALUES (:display_name, :email)'
);
$statement->execute([
':display_name' => $displayName,
':email' => $email,
]);
} catch (PDOException $exception) {
error_log($exception->getMessage());
[] = ;
}
Common Mistakes
Confusing reporting with display
This reports all error levels but does not guarantee browser output:
error_reporting(E_ALL);
For local development, also enable display:
ini_set('display_errors', '1');
Misspelling display_errors
This setting is invalid because its name is misspelled:
display_erros = On
Use:
display_errors = On
Editing the wrong php.ini
PHP running through Apache, PHP-FPM, and the command line can load different configuration files. php --ini describes the CLI configuration; it may not be the configuration used for a browser request.
Create a temporary diagnostic file in a safe local environment:
<?php phpinfo();
Check and the effective values for and . Remove the file afterward, especially on public servers.
Comparisons
| Setting or technique | What it does | Typical development use | Typical production use |
|---|---|---|---|
error_reporting(E_ALL) | Selects error categories PHP reports | Enable all | Usually enable all and log them |
display_errors | Adds errors to browser output | On locally | Off |
log_errors | Writes errors to configured logs | Usually On | On |
ini_set() | Changes an option for the current request when allowed | Quick temporary debugging |
Cheat Sheet
// Local development only: place near the start of the entry script.
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
; php.ini development settings
display_errors = On
display_startup_errors = On
error_reporting = E_ALL
log_errors = On
error_reporting= which errors PHP reports.display_errors= whether browser output includes errors.log_errors= whether errors go to logs.- The valid directive is
display_errors, with tworcharacters inerrors. - Initialize arrays before conditionals if they are used later:
$errors = [];. - Use
$_POST['field'] ?? ''when a submitted field may be absent. - Redirect only after a successful action when the current response does not need to display errors.
FAQ
Why does PHP show errors in the log but not in my browser?
Usually log_errors is enabled while display_errors is disabled. PHP is reporting the problem, but it is configured to send it only to the server log.
Is error_reporting(E_ALL) enough to display PHP errors?
No. It chooses which error levels are reported. display_errors must also be enabled for reported errors to be included in the browser response.
Why does display_errors = On in php.ini not work?
Confirm that the directive is spelled correctly and that you edited the configuration file loaded by Apache or PHP-FPM, not only the CLI PHP configuration. Use phpinfo() temporarily to check the loaded file and effective values.
Should I use E_ALL | E_STRICT?
Usually no. In modern PHP, use E_ALL. Adding E_STRICT is redundant in supported modern versions.
Why is $err undefined on the first page visit?
It is assigned only inside the POST branch, but foreach ($err as ...) runs for both GET and requests. Initialize it before the condition with .
Mini Project
Description
Build a small PHP registration-form handler that validates input, safely initializes form state, catches database errors, and redirects only after a successful insert. It demonstrates the difference between user-facing validation feedback and technical error logging.
Goal
Create a form handler that shows validation errors on the same page and logs PDO failures without exposing raw database messages to users.
Requirements
Initialize the form data and error list for every request.
Validate a display name and email address on POST requests.
Show validation errors above the form.
Use a prepared INSERT statement with PDO exceptions enabled.
Redirect only after a successful database insert.
Log database exceptions and show a safe failure message.
Keep learning
Related questions
Are PDO Prepared Statements Enough to Prevent SQL Injection in PHP?
Learn how PDO prepared statements prevent SQL injection in PHP, what they protect, and the mistakes that still leave MySQL apps vulnerable.
Can You Bind an Array to an IN Clause in PHP PDO?
Learn how PDO handles placeholders in IN() clauses, why arrays cannot be bound directly, and the safe PHP pattern to build dynamic queries.
Choosing the Right MySQL Collation for PHP and UTF-8
Learn how MySQL character sets and collations work with PHP, and how to choose a practical UTF-8 setup for web applications.