Skip to content

PHP Tutorial Error Handling

Production Error Handling

Lesson 195 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

Error messages are useful to you and useful to attackers. A message such as “Access denied for user ‘root’@’localhost'” or a file path tells them how your site is built. On a live site, visitors must never see technical errors.

The two environments

Development (your computer)Production (the live site)
display_errorsOnOff
log_errorsOnOn
error_reportingE_ALLE_ALL
What users seeThe detailed errorA friendly page

Notice that error_reporting stays E_ALL in both. You still want to record everything. You only stop showing it.

Set it up

php.ini
INI
; production php.ini
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/app-errors.log
error_reporting = E_ALL

PHP ships php.ini-production and php.ini-development as starting points.

A friendly error page

index.php
PHP
<?php
set_exception_handler(function (Throwable $e): void {
    error_log((string) $e);              // full detail goes to the log

    http_response_code(500);
    header("Content-Type: text/html; charset=utf-8");

    echo "<h1>Something went wrong</h1>";
    echo "<p>We have been told and are working on it. Please try again later.</p>";
});

Notice the status code 500. Search engines and monitoring tools rely on it. A friendly page that returns 200 OK hides the failure.

Show detail only in development

index.php
PHP
<?php
$isDev = getenv("APP_ENV") === "development";

ini_set("display_errors", $isDev ? "1" : "0");

Read the mode from an environment variable (see Environment Variables), so the same code works everywhere.

Never leak these

  • File paths and line numbers
  • SQL queries and database names
  • Stack traces
  • Library versions
  • Configuration values
index.php
PHP
<?php
function publicMessage(Throwable $e, bool $debug): string
{
    return $debug ? $e->getMessage() : "Something went wrong. Please try again later.";
}

$e = new RuntimeException("SQLSTATE[HY000] [1045] Access denied for user 'root'");

echo publicMessage($e, false) . "\n";
echo publicMessage($e, true);
Output
OUTPUT
Something went wrong. Please try again later.
SQLSTATE[HY000] [1045] Access denied for user 'root'

User mistakes are different

A validation error (“Please enter a valid email”) is not a server error. Show it to the user clearly. Only unexpected failures (database down, bug) get the generic message.

Test the failure path

It is easy to build a site that works when everything is fine. Break something on purpose in a test environment (wrong database password, missing file) and check what a visitor would see.

Monitor

A log nobody reads does not help. Send errors to a service or at least check the log regularly, so you hear about problems before your visitors tell you.

Debug mode flags in other tools

  • WordPress: WP_DEBUG should be false on a live site, and WP_DEBUG_DISPLAY must be false if you log with WP_DEBUG_LOG.
  • Laravel: APP_DEBUG=false in production.
dangerous
Never leave phpinfo() on a live site
A page that calls phpinfo() prints your whole configuration, paths and extensions. Delete test pages after you use them.