— Artikel — № 056

056 —WordPress

wp-config.php debug constants: een werkbare cheatsheet

WordPress kent veertig gedocumenteerde wp-config.php constants. De meeste sites gebruiken er drie. Hier zijn de rest, als cheatsheet om in te plakken.

Bovenaanzicht van verouderd wp-config.php spiekbriefje met PHP debug-constanten, koperen presse-papier, liniaal, potlood, rode lakzegel.
Hero · gestileerd stilleven№ 056

Om 23:41 afgelopen donderdag stuurde een agency-eigenaar een Loom door: WordPress 6.4 op een Hetzner VPS gaf een 500 op /wp-admin, blanke pagina aan de voorkant, error log leeg. De site draaide al drie jaar. Hun fix-pad was de gebruikelijke lus. Plugins uitschakelen door de map te hernoemen via SFTP. Bisecten. Heractiveren. Hopen.

De snellere route zat al in hun wp-config.php, of beter gezegd, ontbrak daar juist. WordPress heeft zo'n veertig gedocumenteerde configuratie-constants, en de meeste productiesites gaan live met drie ervan. De rest is het verschil tussen een wankele WordPress-installatie en een debugbare. Dit is de werkbare cheatsheet voor de wp-config.php constants die hun bytes echt verdienen.

Een debug-blok dat productie overleeft

De klassieke fout is define('WP_DEBUG', true) op een live site. PHP notices en deprecations belanden in de gerenderde HTML, wat JSON endpoints, AJAX, en iedereen met een strikte CSP breekt. De wp-config.php constants die debuggen op productie veilig maken, zien er zo uit:

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', '/var/log/wp/debug.log');
define('WP_DEBUG_DISPLAY', false);
@ini_set('display_errors', '0');

WP_DEBUG_LOG accepteert een pad, niet alleen een boolean. Wijs het naar een locatie buiten de web root. De standaardlocatie is wp-content/debug.log, wat web-toegankelijk is op elke host die dotfiles en *.log nog niet via .htaccess blokkeert. Het pad dat je opgeeft moet schrijfbaar zijn door de PHP-FPM gebruiker, niet je SFTP-gebruiker.

WP_DEBUG_DISPLAY expliciet op false zetten is belangrijk, zelfs als display_errors op PHP-niveau al uitstaat, omdat sommige themes ini_set aanroepen in functions.php tijdens de bootstrap en die instelling teniet doen.

SAVEQUERIES voor de trage pagina die je lokaal niet reproduceert

Als een pagina 14 seconden duurt op staging maar 400ms op je laptop, is de vraag: welke query. SAVEQUERIES vertelt WordPress om elke query, de executietijd, en de call stack die hem triggerde te bewaren in $wpdb->queries.

define('SAVEQUERIES', true);

Daarna, in een must-use plugin of een shutdown hook, dump je de trage queries naar hetzelfde log:

add_action('shutdown', function () {
    global $wpdb;
    if (!current_user_can('manage_options')) return;
    $slow = array_filter($wpdb->queries, fn($q) => $q[1] > 0.05);
    usort($slow, fn($a, $b) => $b[1] <=> $a[1]);
    error_log(print_r(array_slice($slow, 0, 10), true));
});

Zet hem weer uit in productie. SAVEQUERIES verdubbelt het geheugengebruik per request ongeveer, en is het soort constant dat zes maanden aan blijft staan en dan in een profiling-sessie opduikt als mysterieuze overhead.

Geheugenplafonds die het dashboard verbergt

WordPress heeft twee memory-constants. WP_MEMORY_LIMIT geldt voor de voorkant van de site. WP_MAX_MEMORY_LIMIT geldt alleen voor /wp-admin. Beide kunnen worden overschreven door een lagere memory_limit in php.ini, en daar komt ongeveer de helft van de "maar ik heb het toch op 512M gezet" support-tickets vandaan.

define('WP_MEMORY_LIMIT', '256M');
define('WP_MAX_MEMORY_LIMIT', '512M');

Het symptoom van het front-end plafond raken is een half-gerenderde pagina die halverwege de footer afbreekt, zonder fout in het log, omdat PHP stierf voordat de shutdown handler liep. Raak je het admin-plafond, dan krijg je een wit scherm op de plugins-pagina of halverwege een bulkactie. Voor een niet-developer ziet het er allebei uit als "de site is gewoon kapot".

De fatal-error handler die de echte fout verbergt

Sinds WordPress 5.2 wordt een fatale fout in een plugin opgevangen door de recovery-mode handler, die de admin een magic link mailt en een algemene "Er is een kritieke fout opgetreden op deze website" pagina rendert. Goed voor eindgebruikers, ellendig voor developers, want het slikt de echte stack trace uit de response body.

define('WP_DISABLE_FATAL_ERROR_HANDLER', true);

Zet dit aan tijdens het debuggen. Het white screen of death is nuttiger dan een beleefde excuusboodschap, want dan krijgt je debug log tenminste de trace. Zet hem weer uit voordat je de site terug overdraagt aan de klant.

FS_METHOD en de FTP-credentials prompt

Als WordPress een plugin probeert te updaten en de file-owner komt niet overeen met de PHP-FPM gebruiker, vraagt de admin om FTP-credentials. Op een moderne host is dit vrijwel nooit wat iemand wil. Forceer directe filesystem-toegang:

define('FS_METHOD', 'direct');

Als dit faalt met "Could not create directory", klopt de ownership niet. chown -R www-data:www-data wp-content lost het meestal op, maar controleer wat je stack daadwerkelijk als PHP-gebruiker draait voordat je die regel kopieert. Op nginx met PHP-FPM pools is de gebruiker vaak iets minder voor de hand liggend, zoals php-fpm-site1.

Een werkbare basis

Plaats dit blok boven de /* That's all, stop editing! Happy publishing. */ comment in wp-config.php. Pas paden en limieten aan op je host.

// Debug, scoped to logs only
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', '/var/log/wp/debug.log');
define('WP_DEBUG_DISPLAY', false);
@ini_set('display_errors', '0');

// Profile only when needed
define('SAVEQUERIES', false);

// Memory
define('WP_MEMORY_LIMIT', '256M');
define('WP_MAX_MEMORY_LIMIT', '512M');

// Updates and filesystem
define('FS_METHOD', 'direct');
define('DISALLOW_FILE_EDIT', true);
define('AUTOMATIC_UPDATER_DISABLED', false);
define('WP_AUTO_UPDATE_CORE', 'minor');

// Revisions and trash, kept bounded
define('WP_POST_REVISIONS', 10);
define('EMPTY_TRASH_DAYS', 14);

// SSL on the admin, only after the cert is verified working
define('FORCE_SSL_ADMIN', true);

Wat wij uiteindelijk deden

Toen we Pier bouwden voor het via chat aanpassen van verouderde sites, bleef de wp-config.php round-trip ons dwarszitten. Mensen zetten WP_DEBUG aan om een bug te jagen, vergaten het, leverden de site op met display_errors die PHP notices in de JSON API lekten, en zes maanden later vroegen ze zich af waarom hun iOS-app de response niet kon parsen. We zijn het uiteindelijk zo gaan oplossen: het actieve config-blok zichtbaar maken in de file-tree met een zachte waarschuwing als debug constants live staan, en versiegeschiedenis bijhouden op elke wp-config.php-wijziging zodat je de one-liner die om 02:00 's nachts de cron sloopte weer terug kunt draaien.

Het kleinste wat je vandaag kunt doen: open je wp-config.php, voeg het debug-blok van vier regels hierboven toe, wijs WP_DEBUG_LOG naar een pad buiten de web root, en tail hem een uur lang. Je zult verbaasd zijn over wat er al kapot was.

— Vragen —

Vertraagt WP_DEBUG productie?

Marginaal, als WP_DEBUG_LOG naar een snelle schijf wijst en WP_DEBUG_DISPLAY op false staat. De kosten zijn één filesystem-write per notice, verwaarloosbaar totdat je ook SAVEQUERIES aanzet.

Waar moet WP_DEBUG_LOG naartoe schrijven?

Een pad buiten de web root, eigendom van de PHP-FPM gebruiker. /var/log/wp/debug.log is conventie. Laat hem nooit op de standaard wp-content/debug.log staan op een publieke host.

Is DISALLOW_FILE_EDIT genoeg hardening?

Het blokkeert de dashboard-editor als webshell-vector. Combineer hem met DISALLOW_FILE_MODS om ook plugin-installaties te blokkeren, met strakke PHP-FPM bestandsrechten en een echte WAF ervoor.