Hoe de gratis website-check onder de motorkap werkt
Wat gebeurt er als je je site door mijn gratis check haalt? Eén HTTP-request, ruim dertig checks op de broncode en een gewogen score. Ik leg het hele proces uit, inclusief de bug die ik er zelf in vond.
Sinds kort staat er een gratis website-check op deze site. Je vult je URL en je mailadres in en dertig seconden later weet je waarom je site wel of geen klanten oplevert. Ik zou zelf nooit een tool vertrouwen die niet uitlegt hoe hij aan zijn oordeel komt. Dus hier is stap voor stap wat er gebeurt als je op de knop drukt, en waarom ik het zo gebouwd heb.
Je site wordt één keer opgehaald
Het eerste dat de check doet is je site ophalen. Eén keer, met een gewone HTTP-request, precies zoals een crawler van Google dat ook doet:
pagina = fetch(url, {
timeout: 10 seconden,
volg_redirects: true,
user_agent: "ShinBuilds-check"
})
bewaar: eerste 200KB html + alle response headers
Die timeout van tien seconden is bewust: een site die trager is dan dat heeft sowieso een probleem, daar hoef ik niet op te wachten. En ik bewaar alleen de eerste 200 kilobyte van de HTML. Dat is ruim voldoende voor elke normale pagina en het beschermt de check tegen sites die om wat voor reden dan ook gigabytes terugsturen.
Er draait dus geen headless browser die je pagina rendert en screenshots maakt. Dat had gekund, maar ik wil het niet. Een kale HTTP-request is namelijk precies wat elke zoekmachine als eerste ziet. Als jouw site in die ruwe broncode niet te beoordelen is, weet ik al iets belangrijks (daarover straks meer). Bovendien is een browser opstarten traag, en niemand zit te wachten op een check die zelf een halve minuut staat te laden.
Eerst de poort dicht
Voordat er ook maar iets wordt opgehaald, valideert de server je invoer. Niet alleen "is dit een URL", maar ook: heeft mijn server op dit adres wel iets te zoeken?
functie normaliseer(url):
als geen protocol: plak "https://" ervoor
als protocol niet http(s): weiger
als hostname localhost, 10.x, 192.168.x,
172.16-31.x, 127.x of .local: weiger
anders: ga door
Die laatste regel is een beveiliging tegen wat SSRF heet, server-side request forgery. Zonder die check zou iemand mijn server kunnen vragen om "even" interne adressen op te halen die vanaf het internet niet bereikbaar zijn. Zet je een gratis tooltje online, dan komt er vroeg of laat iemand langs die kijkt of het te misbruiken valt.
Het formulier zelf heeft een honeypot: een onzichtbaar veld dat mensen nooit invullen maar bots wel. Staat daar iets in, dan krijgt de bot een vriendelijk nep-succes terug en draait er verder helemaal niks.
Herkennen waar je site op draait
Zodra de HTML en de headers binnen zijn, kijk ik eerst waarmee je site gebouwd is. Elk platform laat vingerafdrukken achter in de broncode:
als html bevat "wp-content" → WordPress
als headers bevatten "x-wix-..." → Wix
als html bevat "data-wf-site" → Webflow
als html bevat "__next" → Next.js
geen match → maatwerk of onbekend
Ik doe dat niet voor de leuk. WordPress-sites hebben andere problemen (plugins, updates, laadtijd) dan Wix-sites (je zit vast aan hun platform en hun servers), en de uitleg in je rapport past zich daarop aan.
Ruim dertig vragen aan je broncode
Dan volgen de checks zelf: ruim dertig stuks, verdeeld over zes categorieën. Elke check is een vraag aan de broncode waar maar één antwoord op mogelijk is. Er komt geen AI aan te pas die "vindt" dat je site beter kan. Een paar voorbeelden.
De technische checks zijn het simpelst. Draait de site op HTTPS? Staat er een viewport meta-tag (zonder die tag laat een telefoon je site zien als desktop-pagina)? Comprimeert de server zijn responses met gzip of brotli?
Bij conversie wordt het interessanter. Daar zoek ik bijvoorbeeld naar actietaal in je knoppen:
knopteksten = alle <a> en <button> teksten
actiewoorden = [plan, boek, offerte, bel,
app me, neem contact, ...]
als geen enkele knop een actiewoord bevat:
fail("geen duidelijke call-to-action")
Ook contactfrictie valt uit de broncode te lezen. Een telefoonnummer als platte tekst betekent dat een mobiele bezoeker je nummer moet overtypen, terwijl een tel:-link er één tik van maakt. Voor mailto: en WhatsApp-links heb ik aparte checks.
Het juridische stuk is verrassend goed te automatiseren. Een KvK-nummer is altijd acht cijfers, een BTW-nummer heeft het patroon NL + negen cijfers + B + twee cijfers, en een privacyverklaring verraadt zich door een link met "privacy" erin. De cookiecheck gaat een stap verder: die kijkt of er trackingscripts laden voordat je toestemming hebt kunnen geven, want daar wordt in de praktijk op gehandhaafd.
Hoe het cijfer tot stand komt
Elke check geeft pass, fail of niet van toepassing terug. Niet van toepassing telt helemaal niet mee, dus je wordt niet afgerekend op iets dat niet te beoordelen was.
Elke check heeft ook een gewicht. Social proof op je homepage en een werkende privacyverklaring wegen 5, een ontbrekende alt-tekst weegt 1. De score is dan:
score = behaalde gewichten / haalbare gewichten × 100
Per categorie draait dezelfde som nog een keer, zodat je ziet waar het lekt. Een 61 als totaalscore zegt weinig, maar een 8% op conversie vertelt je precies waar je moet beginnen.
De keer dat mijn eigen check de fout in ging
Kort na de lancering haalde ik een klantsite door de check. Een site die ik zelf gebouwd heb. Score: 25 van de 100. Geen privacyverklaring, geen KvK-nummer, geen H1. Terwijl ik wist dat het er allemaal op stond.
Wat bleek: die site is een single-page app. De server stuurt een vrijwel lege HTML-shell van nog geen 3KB, en alle inhoud wordt daarna in de browser opgebouwd met JavaScript. De check las die lege shell en concludeerde keurig dat er niks op de pagina stond. Strikt genomen klopte dat nog ook.
De oplossing zit nu in de check:
zichtbare_tekst = html zonder scripts,
styles en tags
als lengte(zichtbare_tekst) < 200 tekens:
markeer als client-side rendered
beoordeel alleen head en headers
laat inhoudelijke checks vallen
meld dit eerlijk in het rapport
Je krijgt dan een beperkte check met een duidelijke melding, in plaats van twintig onterechte fails. Die melding is trouwens meer dan een excuus, want zoekmachines krijgen diezelfde lege pagina te zien en moeten al je content via JavaScript uitvoeren voordat ze iets kunnen indexeren. Dat werkt, maar het is trager en kwetsbaarder dan het hoeft te zijn.
Dit is meteen de belangrijkste beperking van de check: hij beoordeelt de broncode van je homepage. Tekst die pas na een klik of een scroll via JavaScript verschijnt, ziet hij niet. Voor negen van de tien MKB-sites maakt dat niks uit, daar staat alles gewoon in de HTML.
Het rapport kun je niet vervalsen
Onderaan je resultaat zit een knop om alles als PDF-rapport te downloaden. Klein detail waar ik wel even over nagedacht heb: dat rapport wordt niet gebouwd uit de cijfers die jouw browser toevallig doorstuurt. De server draait de complete audit opnieuw en genereert het PDF-bestand uit dat verse resultaat.
Anders houdt niets iemand tegen om met wat aangepaste cijfers een rapport met mijn naam eronder in elkaar te knutselen. Als er ShinBuilds op staat, heeft mijn server het gemeten.
Zelf proberen
De check is gratis, duurt een halve minuut en je krijgt je resultaat direct op de pagina, inclusief het rapport als PDF. Geen nieuwsbrief, hooguit een persoonlijk berichtje van mij als ik iets opvallends in je uitslag zie.
Check je site op shinbuilds.nl/check. En valt je score tegen? De meeste punten zijn sneller opgelost dan je denkt.
Vragen of project in gedachten?
App me, ik reageer binnen 24 uur.