Every guide to a PHP problem starts with "turn on display_errors". On a live site that prints your file paths, your framework, sometimes your database name, to anyone who can make the page error - including whoever is trying to.

The right settings for live

display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/mysite-error.log
error_reporting = E_ALL & ~E_DEPRECATED

E_ALL without deprecations keeps the log readable on an older codebase while still recording everything that is actually wrong.

Watch it while you reproduce

tail -f /var/log/php/mysite-error.log

Leave that running and load the broken page. You get the same information display_errors would have shown, at the same moment, without a visitor ever seeing it.

If you must see it in the browser

Show it to yourself only, by address:

if ((@$_SERVER["REMOTE_ADDR"] ?? "") === "YOUR.IP.HERE") {
    ini_set("display_errors", "1");
    error_reporting(E_ALL);
}
Put that behind a check you cannot forget to remove - a constant in a config file that is not deployed, not a line commented out. A commented-out line gets uncommented.

A log that grows too fast

sort /var/log/php/mysite-error.log | uniq -c | sort -rn | head -5

One notice on every request writes gigabytes in a day. The top line of that output is nearly always the whole problem.

Turn deprecations back on for a day before a PHP upgrade. They are the warnings that become errors in the next version, and reading them early is the cheapest possible preparation.