User-Facing Errors

Error Pages, Correlation IDs and What to Tell the User

The visitor needs a status code, an apology, what did not happen and a reference to quote; the developer needs the exception. A correlation ID, one random value per request, joins them in a response header, the page and every log line. Accept a proxy's X-Request-Id only if it looks like an ID, since whatever a client sends ends up in your logs.

errorpage.php: a 500 page that shows a reference and logs the details against itPHP
<?php
$id = $_SERVER['HTTP_X_REQUEST_ID'] ?? '';               // set by a proxy, or not
if (!preg_match('/^[A-Za-z0-9-]{8,64}$/', $id)) $id = bin2hex(random_bytes(8));
header("X-Request-Id: $id");
set_exception_handler(function (Throwable $e) use ($id): void {
    error_log("[$id] " . $e::class . ": {$e->getMessage()} at "
        . basename($e->getFile()) . ":{$e->getLine()}");
    http_response_code(500);
    echo "<h1>Something went wrong</h1>\n<p>You were not charged. ",
        "If you contact us, quote reference <b>$id</b>.</p>\n";
});
throw new PDOException('SQLSTATE[HY000] [2002] Connection refused');

Served by php -S 127.0.0.1:8213 errorpage.php 2> logs/server.log, fetched with curl 3,008 -si and the header X-Request-Id: 7f3c9a2e-checkout, then searched with grep -F 7f3c9a2e logs/server.log:

Output of 142
HTTP/1.1 500 Internal Server Error
...
X-Request-Id: 7f3c9a2e-checkout
Content-type: text/html; charset=UTF-8
<h1>Something went wrong</h1>
<p>You were not charged. If you contact us, quote reference <b>7f3c9a2e-checkout</b>.</p>
[Wed Sep 23 16:31:06 2026] [7f3c9a2e-checkout] PDOException: SQLSTATE[HY000] [2002] Connection
  refused at errorpage.php:12

The page names no database or path; the log has it all. Add the ID to Monolog 21,402 records with a processor, and send 503 with Retry-After for maintenance and a 4xx only when the visitor can fix the problem.