— Artikel — № 023

023 —Workflow

Manifestbestand voor legacy sites: audit in 20 minuten

Je erft vrijdagmiddag om 16:00 een 9 jaar oude WordPress-site. Om 16:20 hoort er een manifest te liggen dat de volgende persoon vertelt wat er draait.

Open houten kaartenbaklade op linnen, messing labelhouder, uitgewaaierde kaarten, kleirood lint aan één kaart.
Hero · gestileerd stilleven№ 023

Een Nederlands bureau waar we mee werken kreeg vorige maand een WordPress-site overgedragen: negen jaar oud, ongeveer 140 berichten, drie eigen plugins waarvan niemand zich herinnerde wie ze geschreven had, een Magento 1-shop die op een subdomein was vastgeniet, en een PHP-versie die het hostingpaneel discreet als 7.2 vermeldde. De opdracht was "kleine content-update." Twee uur verder waren we nog aan het reverse-engineeren wat de site eigenlijk was.

Dit is het deel van werk aan een legacy site dat niemand opschrijft. De site draait al tien jaar. De helft van de institutionele kennis is met de ontwikkelaar vertrokken. De andere helft staat in een Slack-kanaal dat niemand meer kan terugvinden. Voordat je ook maar één regel code aanraakt, heb je een manifest nodig.

Wat een manifest eigenlijk is

Een manifest is één plain-text bestand, ingecheckt in de repo of opgeslagen bij je projectnotities, dat alles vastlegt wat een opvolger je in het eerste uur zou vragen. Niet de code: de context rond de code. Versies, paden, cron jobs, hardcoded URL's, het e-mailadres waar het contactformulier naartoe post, het ritme waarop het certificaat vernieuwt, de staging-URL waar niemand anders toegang toe heeft.

Noem het MANIFEST.md, SITE.md, README.ops, wat het team daadwerkelijk gaat lezen. De naam is minder belangrijk dan de discipline om er één te schrijven voordat je iets anders doet.

De vastlegging van 20 minuten, op volgorde

Je schrijft een bruikbaar manifest in 20 minuten als je ophoudt het mooi te willen maken. Open een markdown-bestand. Zet een timer. Loop onderstaande zes stappen op volgorde door, zonder zijpaden.

1. Runtime-versies. SSH of FTP erin, dan een phpinfo-bestand droppen of de bestaande bekijken. Vanaf de CLI:

php -v
mysql --version
nginx -v 2>&1 || apache2 -v 2>&1

Heb je alleen FTP, schrijf dan een PHP-probe van één regel en haal de respons op:

<?php echo phpversion(), "\n", PHP_OS, "\n"; ?>

Noteer exacte versies, geen ranges. "PHP 7.4.33" en niet "PHP 7.x." Het patchnummer telt zodra er later CVE-referenties opduiken. De officiële php.net supported versions-tabel vertelt je welke lijn nog beveiligingsfixes krijgt.

2. Database-verbinding. Zoek wp-config.php, settings.php, configuration.php of app/etc/local.xml, afhankelijk van het CMS. Noteer de database-naam, gebruiker, host, en of die naar localhost wijst of naar een aparte host. Is het een aparte host, schrijf dan het IP-adres op, niet alleen de hostname. DNS-records verschuiven.

grep -E "DB_(NAME|USER|HOST)" wp-config.php

Plak het wachtwoord niet in het manifest. Een verwijzing als "zie vault-entry kv/clients/acme/db" is voldoende.

3. .htaccess en rewrite-regels. Hier liggen de lijken. Elke legacy site heeft een .htaccess-blok dat iemand om 2 uur 's nachts heeft toegevoegd om een redirect loop te fixen en nooit gedocumenteerd. Cat het bestand, plak het letterlijk in het manifest onder een eigen kop, en annoteer de niet voor de hand liggende blokken. Zelfs een notitie als "dit blok forceert HTTPS alleen voor /shop/, reden onbekend" is nuttig.

RewriteEngine On
RewriteCond %{HTTP_HOST} ^oldbrand\.nl$ [NC]
RewriteRule ^(.*)$ https://newbrand.nl/$1 [R=301,L]

Apache's eigen mod_rewrite-documentatie is de referentie die je in een ander tabblad open wilt hebben als je een ketting van 12 regels probeert te ontcijferen.

4. Cron jobs. Draai crontab -l op de server. Check /etc/cron.d/ voor system-level jobs. Voor WordPress: kijk in wp_options naar actieve wp-cron taken en eventuele HTTP-loopback scheduler. Magento 1 heeft een eigen cron.sh die je apart documenteert.

5. Plugin- en module-inventaris. Op WordPress met WP-CLI geïnstalleerd:

wp plugin list --format=csv > plugins.csv

Is WP-CLI er niet, dan volstaat een directory listing van wp-content/plugins. Noteer versienummers naast elke regel. Markeer plugins die al meer dan 24 maanden niet zijn bijgewerkt. Dat zijn je audit-kandidaten.

6. Mailflow. Waar stuurt het contactformulier naartoe? Via PHP mail()? Een SMTP-plugin? Een transactionele dienst zoals Postmark of SendGrid? Dit is de vraag die altijd zes weken later opduikt, als een klant vraagt waarom zijn orderbevestiging nooit is aangekomen. Zoek het antwoord één keer op, schrijf het op.

Hardcoded paden en de lange staart

Het zwaarste deel van een manifest zijn niet de voor de hand liggende regels. Het is de lange staart van kleine aannames die in de codebase verstopt zitten en die niemand heeft aangevlagd. Een paar die op elke legacy site terugkomen die we auditen:

  • Absolute paden in includes (require '/home/oldowner/public_html/lib.php') die breken zodra de site naar een andere host verhuist.
  • Hardcoded API-keys in plugin-bestanden, niet in wp-config.
  • SSL-certificaatpaden in eigen snippets, vaak wijzend naar een Let's Encrypt-directory die via drie lagen gesymlinkt is.
  • Die ene cron job die draait op de laptop van een ontwikkelaar, niet op de server, en dat al twee jaar doet.

Grep op de voor de hand liggende patronen en zet de treffers in het manifest:

grep -r "/home/" wp-content/ --include="*.php"
grep -rE "https?://[a-z0-9.-]+\.[a-z]{2,}" wp-content/themes/ --include="*.php"

Waar bewaar je het

Het manifest hoort op drie plekken tegelijk te staan. Ten eerste in de root van de repo als de site in version control zit. Ten tweede in je wachtwoordmanager of vault onder de client-entry, zodat een collega die database-toegang nodig heeft niets hoeft te klonen. Ten derde als geprinte PDF in de projectmap van degene die de klant factureert. Dat laatste klinkt overdreven, totdat de domain registrar-account van de klant twee weken radiostilte geeft en je moet kunnen aantonen wat je hebt geërfd.

Werk het manifest bij elke keer dat je iets wezenlijks verandert. Een manifest dat 18 maanden onaangeraakt is gebleven is slechter dan geen manifest, omdat het zelfverzekerd liegt. OWASP's richtlijnen over inventarissen van legacy applicaties zeggen hetzelfde in meer woorden: documentatie moet een levend artefact zijn, anders wordt het een aansprakelijkheid.

Een uitgewerkt voorbeeld

Hier is een ingekort manifest van een echte WordPress-site die we eerder dit jaar overnamen, namen aangepast:

# acme-nl manifest
Last updated: 2026-04-12

## Runtime
- PHP 7.4.33 (host panel allows up to 8.1)
- MySQL 5.7.39 (MariaDB-compatible)
- Apache 2.4.41
- WordPress 6.2.3 (auto-updates disabled)

## Database
- Host: 145.220.x.x (not localhost)
- DB: acme_prod, user: acme_wp
- Credentials: vault://clients/acme/db
- Daily mysqldump to /backups/acme-YYYY-MM-DD.sql.gz, 30-day retention

## .htaccess quirks
- Forces HTTPS for /winkel/ only (legacy SEO redirect chain)
- Blocks /wp-login.php to non-NL IPs (geoblock added 2024-08)

## Cron
- wp-cron disabled; system cron runs /usr/local/bin/php wp-cron.php every 10m
- nightly mysqldump at 02:30 CET

## Mail
- WP Mail SMTP plugin pointed at Postmark, API key in wp-config (move to env)

## Known landmines
- Theme contains hardcoded path /home/acmenl1/ in functions.php line 312
- Custom plugin "acme-orders" written by previous dev, no source repo, 1.8MB
- SSL cert auto-renews via host panel, not certbot

Dat is het. 30 regels, past op één scherm, maakt de volgende persoon 80% sneller.

De audit die zichzelf terugverdient

Elke legacy site waar we mee gewerkt hebben die een manifest had werd in dagen overgedragen. Bij elke site zonder manifest ging er een week van iemands leven op aan het reconstrueren van ontbrekende context. De 20 minuten die je erin steekt zijn de goedkoopste verzekering die het project ooit zal kopen.

Toen we Pier bouwden om legacy sites via chat te bewerken, kwamen we dit probleem op vrijwel elke site tegen waar we mee dockten. Hoe we het uiteindelijk hebben opgelost: Pier schrijft de eerste keer dat je verbindt een baseline-manifest, legt PHP- en MySQL-versies, schema, .htaccess en de plugin-inventaris automatisch vast, en stempelt een nieuwe entry in de versiegeschiedenis elke keer dat er iets wezenlijks verandert. Het manifest is niet langer iets dat je moet onthouden bij te werken.

Het kleinste wat je vandaag kunt doen: pak de oudste site uit je portfolio, open een terminal, en begin een MANIFEST.md met de drie versie-commando's bovenaan dit stuk. Voordat je koffie koud is, heb je iets bruikbaars.

— Vragen —

Wat is het verschil tussen een site-manifest en een README?

Een README legt uit wat de site doet. Een manifest legt vast wat hem draait: versies, paden, cron jobs, dependencies, mailflow. Een opvolger heeft beide nodig, maar het manifest eerst.

Waar moet het manifestbestand staan?

Op drie plekken tegelijk: in de root van de repo voor ontwikkelaars, in je wachtwoord-vault voor wie credentials nodig heeft, en als PDF in de projectmap van degene die de klant factureert.

Hoe vaak moet ik het bijwerken?

Elke keer dat je iets wezenlijks verandert: PHP-upgrade, plugin-installatie, certificaatvernieuwing, cron-aanpassing, hostingmigratie. Een verouderd manifest is slechter dan geen, omdat het zelfverzekerd liegt.