Legacy sites
CMS herkennen aan de mappen: veldgids voor migraties
Je krijgt FTP-credentials van een klant en niemand weet meer wat er draait. Je hebt negentig seconden voor de call doorgaat. Lees de mappenstructuur.

Je krijgt FTP-credentials van een klant die zich niet meer kan herinneren waar hun site op draait. De login werkt nog. De site laadt. Niemand aan hun kant weet of het WordPress is, Drupal 7, of een of andere maatwerk-PHP die een freelancer in 2014 schreef en daarna verdween.
Je hebt negentig seconden voor de call hervat wordt. Je hebt geen negentig seconden nodig. Je moet de mappenstructuur lezen.
Waarom mappen sneller verraden dan de markup
De gerenderde pagina liegt. Een custom theme kan WordPress op Squarespace laten lijken, Drupal op WordPress, en Magento op een brochure. View-source is gescrubd door de helft van alle optimalisatie-plugins ter wereld. Wappalyzer gokt, en op verouderde infrastructuur gokt het ongeveer een derde van de tijd verkeerd.
De directory tree op disk liegt niet. Elk CMS dat we in migratiewerk tegenkomen laat een eigen vingerafdruk achter in het filesystem, lang voordat er een byte gerenderd wordt. Zodra je die vingerafdrukken op het oog kunt lezen, knip je de discovery-fase van een legacy-audit terug van een middag tot één SSH-sessie.
WordPress: de wp- prefix verraadt zichzelf
De makkelijkste leesopdracht in het verouderde web. Drie mappen op hetzelfde niveau in de docroot, allemaal met prefix wp-, plus een config-bestand:
$ ls /var/www/html
index.php wp-admin/ wp-config.php wp-content/ wp-includes/ wp-login.php
In wp-content/ vind je plugins/, themes/, uploads/, soms mu-plugins/. Zekerheid: maximaal. Zelfs zwaar aangepaste stacks (Bedrock, ClassicPress, een geforkte WordPress onder een andere naam) houden de wp- prefix, omdat te veel core-code die paden hardcodeert.
Lees wp-includes/version.php voor de exacte build. Alles onder 6.x betekent dat je naar security-schuld kijkt waar de klant al jaren niet voor heeft betaald. De WordPress security-pagina laat zien welke major-versies nog backports krijgen; sites onder 5.0 leven op geleende tijd.
Drupal: core/ versus sites/all/
Twee tijdperken Drupal die qua filesystem vrijwel niks met elkaar gemeen hebben.
Drupal 7 en eerder zetten contrib modules onder sites/all/modules, themes onder sites/all/themes, en de site config in sites/default/settings.php. Er is geen core/ map. Een CHANGELOG.txt staat in de docroot en pocht over welke Drupal-versie wanneer uitkwam.
# Drupal 7
$ ls
CHANGELOG.txt includes/ misc/ modules/ profiles/ scripts/ sites/ themes/ index.php
Drupal 8, 9, 10, 11 introduceerden een top-level core/ map en een vendor/ die door Composer wordt aangestuurd. Contrib-code staat onder modules/contrib en themes/contrib:
# Drupal 10
$ ls
composer.json core/ modules/ profiles/ sites/ themes/ vendor/ web.config
Zie je Drupal 7? Behandel het als een migratie-noodgeval. Drupal 7 bereikte end-of-life op 5 januari 2025 na meerdere verlengingen, het project levert geen security patches meer, en het contrib-ecosysteem is uitgestorven. We treffen Drupal 7 nog steeds in productie bij universiteiten, gemeenten, en elk bedrijf waarvan het oorspronkelijke bureau is verdwenen.
Joomla: configuration.php in de root
Het meest verraderlijke teken is een configuration.php bestand dat naast een administrator/ map staat. Geen ander CMS gebruikt precies die bestandsnaam in de docroot.
$ ls
administrator/ components/ configuration.php htaccess.txt index.php
language/ libraries/ modules/ plugins/ templates/
Die configuration.php bevat database credentials in platte PHP. Open hem voorzichtig en commit hem nooit. Joomla 3.x bereikte end-of-life in augustus 2023 en draait nog steeds op een verrassend aantal vergeten subdomeinen.
Magento: bin/magento of het mage-script
Magento 1 en Magento 2 delen een naam en verder niks. De filesystemen zijn compleet verschillende stacks.
# Magento 1 (EOL juni 2020)
$ ls
app/ errors/ index.php js/ lib/ mage media/ skin/ var/
# Magento 2
$ ls
app/ bin/ generated/ lib/ pub/ var/ vendor/
De verraders: Magento 1 heeft een mage CLI-executable in de root en een skin/ map met frontend assets. Magento 2 heeft bin/magento, geen skin/, en een app/etc/env.php in plaats van M1's app/etc/local.xml. Vind je Magento 1, dan migreer je niet, dan bouw je opnieuw. Adobe stopte met patchen in juni 2020 en de Adobe Commerce lifecycle policy heeft de bonnetjes.
De long tail
Je gaat ze allemaal in het wild tegenkomen. Onthoud de kleinste unieke signatuur per stuk.
- TYPO3:
typo3/,typo3conf/,typo3temp/,fileadmin/. Onmiskenbaar. Veel gezien bij Duitstalige universiteiten en B2B-uitgevers. - Concrete CMS (voorheen concrete5):
concrete/enapplication/naast elkaar. - Craft CMS: een
craftexecutable in de root, plusconfig/,templates/,web/. Composer-gedreven en modern genoeg dat het meestal in orde is. - ModX:
core/,manager/,connectors/,assets/. Deconnectors/map is de gever. - SilverStripe:
framework/+cms/+mysite/op 3.x;app/+public/+vendor/op 4+. - PrestaShop:
classes/,controllers/,override/, en een admin-map die hernoemd is naar iets alsadmin1234/in naam van security through obscurity. - ExpressionEngine:
system/ee/enthemes/ee/.
Vertrouw niet op de X-Powered-By header, de <meta name="generator"> tag of de favicon. Themes liegen. Reverse proxies liegen. Het filesystem is de enige eerlijke getuige, en zelfs dat kan in de war raken door een Bedrock-achtige WordPress-installatie die de docroot in web/wp/ hijst.
De fingerprint in vijf minuten
Plak dit in een SSH-sessie. Het is bewust dom: geen dependencies, geen network calls, geeft één woord terug.
#!/usr/bin/env bash
# fingerprint.sh - point at a docroot, get a one-word CMS guess
ROOT="${1:-.}"
cd "$ROOT" || exit 1
[ -d wp-admin ] && [ -d wp-includes ] && echo "wordpress" && exit
[ -f configuration.php ] && [ -d administrator ] && echo "joomla" && exit
[ -f core/lib/Drupal.php ] && echo "drupal-8plus" && exit
[ -f CHANGELOG.txt ] && [ -d sites/all ] && echo "drupal-7" && exit
[ -f bin/magento ] && echo "magento-2" && exit
[ -f mage ] && [ -d app/code/local ] && echo "magento-1" && exit
[ -d typo3 ] && [ -d typo3conf ] && echo "typo3" && exit
[ -d concrete ] && [ -d application ] && echo "concrete-cms" && exit
[ -f craft ] && [ -d templates ] && echo "craft" && exit
[ -d manager ] && [ -d connectors ] && echo "modx" && exit
[ -d classes ] && [ -d override ] && echo "prestashop" && exit
echo "unknown - dig deeper"
Weten waar je naar kijkt bepaalt alles wat daarna komt. Een Drupal 7 site aan het einde van zijn leven met een Composer-loos filesystem is een rebuild op Drupal 10 of een zijwaartse stap naar een statische front-end met een headless CMS. Een Joomla 3.x site is bijna altijd goedkoper om te porten dan om te upgraden. Een WordPress 6.x met dertig actieve plugins is een hardening-klus, geen migratie. Eerst de tree lezen voorkomt dat je het verkeerde project offreert.
Als wij een legacy migratie draaien, is het fingerprint-script hierboven het eerste dat we starten zodra de credentials binnen zijn. Bij een recente Joomla-rebuild wist het ons binnen tien minuten te vertellen dat het template niet meer te redden was en dat de database drie verlaten componenten herbergde die data lekten, het soort inzicht dat je liever hebt voordat je een offerte stuurt.
Open een terminal, ssh naar de volgende klantserver waar je toegang toe hebt, plak het script. Wat het uitprint bepaalt het volgende gesprek.
Kern
Markup liegt, headers liegen, maar de mappenstructuur op disk niet. Eerst de mappen lezen, dan pas de migratie plannen.
FAQ
Kun je een CMS herkennen aan alleen de URL?
Soms. Paden als /wp-admin of /administrator verraden de engine. Maar veel sites hernoemen of verbergen admin-URLs, dus blijft een filesystem-check de enige betrouwbare test.
Wat als er geen SSH- of FTP-toegang is?
Probeer bekende asset-paden in de browser (bijv. /wp-content/uploads/, /sites/default/files/). Blokkeert de host die ook? Val terug op response headers en de generator meta tag.
Is Drupal 7 echt dood?
Drupal.org stopte begin 2025 met core security patches. Een paar third-party leveranciers verkopen nog extended support, maar het contrib-ecosysteem is uitgestorven en de meeste modules krijgen geen updates meer.
Hoe fingerprint je een headless of statische site?
Een puur statische export heeft geen CMS-fingerprint op disk. Kijk in plaats daarvan naar de build pipeline: package.json, next.config.js, gatsby-config.js, astro.config.mjs of een Hugo config.toml.
Werkt het script ook bij multisite-installaties?
Deels. WordPress multisite heeft nog steeds wp-admin en wp-includes, dus wordt het correct herkend. Drupal multisite verbergt per-site config onder sites/<domain>/, dus check sites/ op meer dan alleen default/.