Skip to content
70% launch offer · $10/hour · Get a quote

White screen after an update: reading the error log like an engineer

A blank page is not the absence of information — it is information written somewhere you are not looking yet. Where OpenCart, PHP and the web server each keep it, and how to get from an empty screen to a file and a line number.

Sercan Öztürk
Head of SRE & Platform Engineering · · 5 min read

terminal tailing error.log after a failed update

The update finished, you reloaded the storefront, and the browser shows nothing. No error, no layout, no OpenCart. It feels like the site is gone. It is not: PHP stopped in the middle of building the page, and the reason it stopped was written down — just not on the screen in front of you.

This is the routine we run on a call-out, in order. It takes minutes rather than hours, and it works the same way on OpenCart 3.0.x and 4.x.

A white screen is a 500 you cannot see

Start with the status code, because it splits the problem in two. Ask for the storefront and the admin separately — if one of them answers, you have already halved the search space.

curl -s -o /dev/null -D - "https://example.com/" | head -n 1
curl -s -o /dev/null -D - "https://example.com/index.php?route=common/home" | head -n 1
curl -s -o /dev/null -D - "https://example.com/yonetim/index.php" | head -n 1

HTTP 500 means PHP died and the web server knows about it, so the reason is in the FPM and web-server logs. HTTP 200 with an empty body is the more confusing case: PHP ran, produced nothing, and error output is switched off. That is almost always a fatal error with display_errors disabled — which is the correct production setting, and exactly why the next steps exist.

While you are here, rule out the two things that are not PHP at all. A CDN or full-page cache can keep serving the empty response long after the site itself is fixed, and a browser will happily cache it too. curl bypasses both: if curl returns HTML and your browser still shows white, the problem sits in front of OpenCart rather than inside it.

Three logs, in this order

OpenCart writes its own log to system/storage/logs/error.log, or wherever DIR_LOGS points in config.php if the storage directory has been moved out of the web root. That file is the first stop but not the whole story: OpenCart's handler only catches what PHP hands it after the framework has started. A parse error, or a crash during bootstrap, never reaches it and appears only in the PHP or web-server log.

tail -n 100 /var/www/store/system/storage/logs/error.log
tail -n 100 /var/log/php8.1-fpm.log
tail -n 200 /var/log/nginx/error.log      # Apache: /var/log/apache2/error.log
If error.log is empty and its timestamp is old, do not conclude that nothing happened. Logging may be switched off in Settings, or the file may not be writable by the web user. Check with ls -l before you believe an empty log.

Turning the errors back on, for ten minutes

OpenCart's error handling lives in System, Settings, edit store, Server tab: Display Errors, Log Errors and Error Log Filename. Those three map to config_error_display, config_error_log and config_error_filename in oc_setting. If the admin is white too you cannot get there, so set them in SQL — or make PHP speak directly in the entry file.

UPDATE oc_setting SET `value` = '1'
WHERE `code` = 'config' AND `key` IN ('config_error_display','config_error_log') AND store_id = 0;
<?php
// index.php — temporary, first lines only
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
Take both changes out as soon as you have the message. Displayed errors leak absolute paths, database names and sometimes credentials to anyone who loads the page while a bot is scanning it — and bots scan constantly.

Reading the line you found

An OpenCart log entry has a fixed shape: timestamp, severity, message, then the file and the line.

2026-08-04 23:14:07 - PHP Fatal error:  Uncaught TypeError: count(): Argument #1
($value) must be of type Countable|array, null given in
/var/www/store/extension/acme_shipping/catalog/model/shipping/acme.php:118
Stack trace:
#0 /var/www/store/system/engine/loader.php(122): Acme\Shipping->getQuote(Array)

Three habits make this quick. Read the first fatal after the deployment timestamp rather than the newest line in the file, because the later entries are usually consequences of the first. Look at the path before the line number: anything under extension/, or under catalog/controller/extension in 3.0.x, is third-party code and therefore something you can switch off. And ignore Notice and Deprecated entries while you are hunting — they are noise on almost every store and they were there yesterday too.

The five causes behind most post-update white screens

  1. 1The PHP version moved with the update. OpenCart 4 requires PHP 8, while a 3.0.x store and its extensions were written for PHP 7. Functions removed in PHP 8.0 — each(), create_function(), curly-brace string offsets — become fatal errors in old extension code.
  2. 2A stale modification cache, in 3.0.x only. system/storage/modification holds patched copies of core files; once new core files are installed, those copies no longer match. Clear the folder, then refresh from Extensions, Modifications.
  3. 3Template and data cache. system/storage/cache can still be serving compiled templates from the previous version.
  4. 4Memory. A bigger catalog or one new extension pushes past memory_limit and PHP stops mid-page, usually with no output at all.
  5. 5A missing PHP module or wrong ownership after restoring an archive: Call to undefined function curl_init(), or failed to open stream: Permission denied.
cd /var/www/store
rm -rf system/storage/cache/*
rm -rf system/storage/modification/*        # OpenCart 3.0.x only
php -r 'foreach (["curl","gd","zip","mbstring","openssl"] as $e) printf("%-10s %s\n", $e, extension_loaded($e) ? "ok" : "MISSING");'
chown -R www-data:www-data . && find . -type d -exec chmod 755 {} \; && find . -type f -exec chmod 644 {} \;

Two traps in that block. php -v and php -r report the command-line PHP, which is often neither the version nor the module set nginx uses — check the pool with php-fpm8.1 -v and the socket named in your vhost. And in 3.0.x, emptying system/storage/modification without pressing Refresh leaves your OCMOD customizations simply not applied: the store loads perfectly, and features quietly disappear.

OpenCart 3.0.3.x
PHP 7.4
OpenCart 4.0.x
PHP 8.0+
First log to open
system/storage/logs/error.log

Getting back online, then finding the cause

If the store is taking orders, restore first and investigate afterwards. Recovery and diagnosis are two different jobs and they do not have to happen in the same hour.

  1. 1Restore the pre-update file archive and database dump, then confirm that both the storefront and the admin answer 200.
  2. 2Copy that restored state to a staging URL and repeat the update there with display errors on.
  3. 3If the fatal points at an extension, disable it in the copy and repeat — one extension at a time. The log names the file, so the search space is small.
  4. 4Fix it on the copy, then deploy again in a quiet hour with maintenance mode on and the log open.

The routine that removes the surprise

  • A staging copy of the real store, not a demo: same extensions, same theme, same PHP version.
  • A database dump and a file archive taken immediately before the update — and tested by restoring them once.
  • An empty error.log at the start, so that everything in it afterwards belongs to this update.
  • A written extension inventory: name, version, author, last update date.
  • Maintenance mode during the switch, and a curl check of both storefront and admin before it comes off.
mysqldump --single-transaction --routines opencart | gzip > /backup/oc-$(date +%F-%H%M).sql.gz
tar czf /backup/oc-files-$(date +%F-%H%M).tar.gz -C /var/www store
: > /var/www/store/system/storage/logs/error.log

When to write to us

If the log line makes no sense, or it points at a file nobody on your team wrote, send us the last hundred lines of error.log with your OpenCart and PHP versions. During the launch campaign work is billed hourly at $10 per hour plus VAT, and you approve the estimate before anything starts. First response is within two hours, Monday to Saturday, 09:00–22:00 (GMT+3). If the store is down right now, say so in the first line.

Sercan Öztürk
Head of SRE & Platform Engineering

CI/CD, SRE and GitOps; SLO/SLI and production incident command.