— Artikel — № 130

130 —WordPress

wp-config.php debug-constanten: de negen die wij altijd zetten

Een site valt in je inbox: SFTP-creds, vage symptomen, geen documentatie. Voor we ook maar één regel pluginbroncode lezen, zetten we negen wp-config-constanten.

Linnen bureau van boven met ruitjesschrift vol PHP debug-constanten, wp-config codevel, emaillen plaatje, pen, rode DEBUG-stempel.
Hero · gestileerd stilleven№ 130

Een site valt in je inbox. SFTP-creds, vage symptomen, geen documentatie: "de zoekbalk geeft een wit scherm op mobiel, maar werkt op desktop soms wel." Je logt in, opent wp-config.php, en voor je ook maar één regel pluginbroncode van iemand anders leest, zet je negen constanten. De site gedraagt zich het komende uur anders, en de audit trail ook. Dit is de cheatsheet.

De twee die we als eerste zetten

Twee constanten doen het meeste werk op een vers overgenomen legacy site. Ze zetten logging aan zonder die naar buiten te lekken, en ze bevriezen het oppervlak dat het grootste risico vormt om de boel te verergeren terwijl je de codebase doorleest.

WP_DEBUG_LOG, met een echt pad

WP_DEBUG op zichzelf zet alleen PHP's error_reporting aan. De bruikbare constante is WP_DEBUG_LOG, die die errors naar een bestand wegschrijft. De standaardlocatie is /wp-content/debug.log, op de meeste hosts publiek leesbaar tenzij de vorige developer eraan dacht om dat te blokkeren. Vertrouw daar niet op. Wijs de log naar een pad boven de docroot.

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', '/home/site/private/wp-errors.log' );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );

Die laatste ini_set doet ertoe. WP_DEBUG_DISPLAY kan door PHP ongedaan worden gemaakt als een plugin later in de request ini_set('display_errors', 1) aanroept. Het paar hoort bij elkaar. De officiële WordPress debug-docs bevatten de canonieke uitleg.

DISALLOW_FILE_EDIT, meteen

Eén regel dashboard-hardening die een verrassend aantal post-compromise-aanvallen tegen legacy WordPress blokkeert: de in-dashboard theme- en plugin-editors. Als die aanstaan, kan een gestolen admin-sessie live PHP in functions.php plakken en met twee klikken de site overnemen. Zet ze op slot.

define( 'DISALLOW_FILE_EDIT', true );

De zeven die volgen

De eerste twee zijn niet onderhandelbaar. De volgende zeven zetten we met enig oordeel, ongeveer in deze volgorde.

SCRIPT_DEBUG

Zegt WordPress om de ongeminificeerde core-CSS en JS te laden. Als een adminscript breekt omdat een plugin jQuery twee keer heeft enqueued, kun je de regel lezen die het brak in plaats van met je ogen te knijpen naar wp-admin.min.js:1:14238.

define( 'SCRIPT_DEBUG', true );

SAVEQUERIES

Elke database-query die in een request gedaan wordt, komt op $wpdb->queries te staan, inclusief de SQL, de tijd en de aanroepende functie. Het is zwaar. Laat het niet aanstaan in productie. Maar voor een middag "waarom duurt deze pagina 11 seconden" brengt niets je zo dicht bij het antwoord.

define( 'SAVEQUERIES', true );

DISALLOW_FILE_MODS

De grotere hamer. Blokkeert plugin- en theme-installs, updates en verwijderingen vanuit het dashboard, bovenop de editor-lockdown. We zetten dit op elke overgenomen site totdat we besloten hebben welk auto-update-pad we volgen. Het is het verschil tussen "een breach onderzoeken" en "een breach onderzoeken terwijl de aanvaller nog plugins aan het installeren is." De wp-config.php-referentie documenteert het exacte gedrag, en het is de moeite waard om eenmalig door te lezen.

define( 'DISALLOW_FILE_MODS', true );

WP_AUTO_UPDATE_CORE

Drie nuttige waarden: true (alle updates), 'minor' (alleen security en minor, de WordPress-default) en false (niets). Op een onbekende site zet je hem op 'minor', zodat security-patches blijven binnenkomen terwijl je de codebase doorleest. Laat hem niet op false staan en vergeet hem niet; niemand anders gaat er nog aan denken.

define( 'WP_AUTO_UPDATE_CORE', 'minor' );

FORCE_SSL_ADMIN

Als de site een geldig certificaat heeft (en in 2026 heeft die dat), forceert dit elke adminrequest en elke auth-cookie over HTTPS. Sites die de cPanel-en-shared-IP-tijd hebben overleefd, hebben dit vaak niet ingesteld, en het loginformulier POST nog altijd over poort 80 door een CDN dat stilletjes "upgrade". Maak het expliciet.

define( 'FORCE_SSL_ADMIN', true );

WP_POST_REVISIONS

Zonder limiet kan een spraakzame redacteur één pagina met 600 revisie-rijen achterlaten. Beperk het. Vijf is ruim voor de meeste redactionele workflows.

define( 'WP_POST_REVISIONS', 5 );

WP_MEMORY_LIMIT en WP_MAX_MEMORY_LIMIT

We tellen ze als één, omdat we ze nooit los zetten. WP_MEMORY_LIMIT is het front-end-plafond; WP_MAX_MEMORY_LIMIT is dat van de admin (imports, media, WP-CLI). Verhoog beide voordat je memory-fatal-plugins gaat debuggen, anders ben je een uur kwijt aan een fatal die de php.ini van de host er van onderaf al had afgekapt.

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

Het hele blok, klaar om te plakken

Plak dit boven de regel /* That's all, stop editing! */. Houd je echte DB-credentials en table prefix erboven.

/* --- inherited-site debug block --- */
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', '/home/site/private/wp-errors.log' );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );

define( 'SCRIPT_DEBUG', true );
define( 'SAVEQUERIES', true );

define( 'DISALLOW_FILE_EDIT', true );
define( 'DISALLOW_FILE_MODS', true );
define( 'WP_AUTO_UPDATE_CORE', 'minor' );
define( 'FORCE_SSL_ADMIN', true );

define( 'WP_POST_REVISIONS', 5 );
define( 'WP_MEMORY_LIMIT', '256M' );
define( 'WP_MAX_MEMORY_LIMIT', '512M' );
/* --- end debug block --- */

Wat je terugdraait voor je de site overdraagt

Drie van de negen horen niet aan te blijven na het debugvenster:

  • SAVEQUERIES uit. Het alloceert geheugen voor elke query op elke request.
  • SCRIPT_DEBUG uit. Ongeminificeerde core-assets zijn trager voor bezoekers.
  • WP_DEBUG en WP_DEBUG_LOG uit, of op zijn minst gericht op een geroteerd pad. Een debug-log die zes maanden lang groeit, is zelf een incident.

De andere zes (DISALLOW_FILE_EDIT, DISALLOW_FILE_MODS, WP_AUTO_UPDATE_CORE, FORCE_SSL_ADMIN, WP_POST_REVISIONS, de memory-limits) blijven aan. Ze kosten niets en ze blijven jarenlang uitbetalen.

De mu-plugin die SAVEQUERIES bruikbaar maakt

SAVEQUERIES op zichzelf zet alleen data op $wpdb. Om die uit te lezen, zet je een mu-plugin van één bestand neer op /wp-content/mu-plugins/000-savequeries-dump.php:

<?php
add_action( 'shutdown', function () {
    if ( ! defined( 'SAVEQUERIES' ) || ! SAVEQUERIES ) return;
    if ( ! current_user_can( 'manage_options' ) ) return;
    global $wpdb;
    $total = 0;
    foreach ( $wpdb->queries as $q ) $total += $q[1];
    error_log( sprintf(
        '[savequeries] %d queries, %.3fs total on %s',
        count( $wpdb->queries ),
        $total,
        $_SERVER['REQUEST_URI'] ?? 'cli'
    ) );
} );

Nu logt elke adminpageview het aantal queries en de totale DB-tijd naar hetzelfde bestand waar WP_DEBUG_LOG naartoe schrijft. Tail het terwijl je door het dashboard klikt, en de trage query meldt zichzelf.

Waar Pier in past

Toen we Pier bouwden voor werk aan overgenomen WordPress-sites, was deze lijst het eerste wat we in de connect-flow hebben verdraad. De app leest wp-config.php bij het docken, markeert de constanten die ontbreken of op risicovolle waarden staan, en biedt een one-click-patch. Elke wijziging aan het bestand gaat de version history in, dus zowel het blok hierboven als het blok eronder zijn terug te draaien zonder dat je in een SFTP-back-up hoeft te grepen.

Open de volgende wp-config.php die je overneemt, grep op deze negen constanten en noteer welke ontbreken. Dat is de audit. Ze zetten is het makkelijke deel.

— Vragen —

Waar moet WP_DEBUG_LOG naartoe wijzen?

Boven de docroot, het liefst buiten elke directory die door de webserver geserveerd wordt. De default /wp-content/debug.log is op de meeste shared hosts publiek leesbaar tenzij iets dat expliciet blokkeert.

Blokkeert DISALLOW_FILE_EDIT ook plugin-installs?

Nee. DISALLOW_FILE_EDIT blokkeert alleen de in-dashboard code-editor voor themes en plugins. Om ook installs, updates en verwijderingen te blokkeren, zet je DISALLOW_FILE_MODS op true.

Is het veilig om SAVEQUERIES aan te laten in productie?

Nee. Het alloceert geheugen voor elke query op elke request en kan het geheugengebruik op langlopende adminpagina's laten uitdijen. Zet het aan voor een debugvenster, en daarna weer uit.