
Een contactformulier op een static website, zonder afhankelijk te zijn van derden
Static websites hebben veel voordelen. Ze zijn snel, veilig, eenvoudig te hosten en prettig te onderhouden. Met een generator als Hugo staat er in no-time een razendsnelle site zonder database of complexe backend.
Maar er is één klassiek pijnpunt: het contactformulier.
Zonder server-side logica ben je al snel aangewezen op externe diensten zoals Formspree, Getform of de forms-functionaliteit van Netlify. Dat werkt. Vaak zelfs gratis of voor weinig geld.
Toch wringt daar iets fundamenteels.
Je levert de regie over je data uit.
Een externe partij bepaalt:
- of een formulier überhaupt wordt geaccepteerd
- wanneer iets als spam wordt gezien
- hoe en waar jouw data wordt opgeslagen
- welke metadata wordt gelogd
Ik vind het een vreemde gedachte dat een derde partij bepaalt of iemand mij mag contacteren.
Dus heb ik mijn contactformulier opnieuw actief gemaakt. Volledig onder eigen beheer. De kern: n8n.
Waarom n8n als backend voor een contactformulier
n8n is in de basis een workflow-automation tool. Denk aan een open-source alternatief voor Zapier. Maar omdat je het self-hosted kunt draaien, leent het zich uitstekend als lichtgewicht backend voor een static site.
Wat mij aanspreekt:
- self-hosted
- volledig transparant
- geen verplichte database
- flexibel uitbreidbaar
- volledige controle over logging, validatie en beveiliging
Mijn contactformulier post rechtstreeks naar een n8n webhook. Alles wat daarna gebeurt, bepaal ik zelf. Geen black box. Geen verborgen “anti-spam scoring”. Geen beleid dat plots verandert.
De workflow: stateless, maar streng
De workflow heb ik bewust stateless opgezet. Geen database, geen externe opslag, geen sessies. Alles wordt gevalideerd op het moment van binnenkomst.
De workflow-JSON stel ik binnenkort beschikbaar, ik wil namelijk nog wel mijn workflow praktisch getest hebben. Maar de globale afhandeling ziet er zo uit:
- Webhook ontvangt de POST-request
- Velden worden genormaliseerd
- Verkeer wordt gefilterd en gevalideerd
- Alleen geldige berichten worden doorgelaten
- Een e-mail wordt verstuurd
- De client krijgt een nette JSON-response terug
Eenvoudig in opzet. Strikt in uitvoering.
Ingebouwde beveiligingslagen
Dit formulier is nadrukkelijk niet “simpel”. Een publiek endpoint moet defensief ontworpen zijn. Zeker zonder traditionele backend.
1. IP-allowlist
Alleen verkeer van bekende IP-adressen wordt geaccepteerd. Alles daarbuiten krijgt direct een 403-response.
Geschikt voor:
- eigen websites
- eigen servers
- testomgevingen
Geen bekende herkomst? Geen toegang.
2. Honeypot-veld
Het formulier bevat een verborgen veld dat normale gebruikers nooit invullen. Bots vaak wel.
Het resultaat:
- geen foutmelding
- wel een “ok” response
- geen verdere verwerking
Bots krijgen geen signaal dat ze zijn afgevangen. Dat maakt het minder aantrekkelijk om te blijven proberen.
3. Strikte veldvalidatie
Naam, e-mail en bericht worden inhoudelijk gecontroleerd op:
- minimale lengte
- geldig e-mailformaat
Onjuiste input krijgt een duidelijke 400-response. Geen vage “er ging iets mis”.
4. Nonce met vervaltijd
Elke formulier-submit bevat:
- een nonce
- een expiry timestamp
- een HMAC-signature
De nonce:
- is tijdsgebonden
- is cryptografisch beschermd
- vereist een server-side secret
Dit voorkomt replay-aanvallen en scripted spam.
5. Timing-safe signature checks
Alle cryptografische vergelijkingen worden uitgevoerd met timing-safe methodes. Geen subtiele lekken via timing-attacks.
Klein detail, groot verschil.
6. Optionele HMAC over de volledige payload
Naast de nonce kan de volledige payload worden gesigneerd. Dat is vooral handig wanneer je het formulier vanaf meerdere frontends gebruikt.
Zo weet je zeker dat de inhoud onderweg niet ongemerkt is aangepast.
7. Rate limiting per IP
Per IP-adres geldt een maximum aantal requests per uur.
Geïmplementeerd via n8n workflow static data. Dus:
- geen aparte database
- geen Redis
- geen extra infrastructuur
Wel effectief.
8. Controlled responses
Elke foutconditie heeft een eigen HTTP-statuscode:
- 401 voor authenticatieproblemen
- 403 voor IP-blokkades
- 429 voor rate limiting
Geen generieke foutpagina’s, maar voorspelbaar gedrag.
Waarom geen externe form service meer
Samengevat:
- mijn data blijft van mij
- geen afhankelijkheid van beleid van derden
- geen verborgen logging
- geen lock-in
- volledig inzicht in de hele keten
Ja, dit kost meer denkwerk dan een embed-scriptje plakken.
Maar het past bij hoe ik mijn infrastructuur wil inrichten: bewust, minimalistisch en onder eigen controle. Net zoals ik eerder koos voor een static site met Hugo in plaats van een traditionele CMS-setup.
Slot
Een static website hoeft geen concessies te doen op functionaliteit of veiligheid.
Met een tool als n8n bouw je precies datgene wat je nodig hebt. Niet meer. Niet minder.
Mijn contactformulier is weer live. Onder mijn voorwaarden.
Wie dit interessant vindt, mag me nu ook daadwerkelijk bereiken. Via dat formulier.