← Terug naar kennisbank BEVEILIGING

Brute-force op wp-login.php — preventie zonder Wordfence

Elke WordPress-site krijgt het: bots die continu wachtwoorden raden op /wp-login.php en /xmlrpc.php. Het standaard advies is "installeer Wordfence", maar Wordfence kost al gauw 200-500 MB extra geheugen per pageload, vertraagt je site en doet honderd dingen die je waarschijnlijk niet nodig hebt. Vier lichtgewicht methoden om brute-force tegen te houden zonder zware plugin — met access.log-diagnose, Cloudflare-rules en .htaccess-snippets die je vanavond nog kunt instellen.

Kort antwoord: Brute-force op wp-login.php stop je het effectiefst met Cloudflare rate-limiting (5 POSTs/min per IP → block) gecombineerd met een .htaccess-blokkering van xmlrpc.php. Geen Cloudflare? Dan basic-auth als extra wachtwoord-laag voor wp-login.php via .htaccess. Wordfence is voor de meeste sites overkill — 200-500 MB extra geheugen per pageload voor iets dat je ook serverside kunt oplossen. Loop je er niet uit? Ik stel dit binnen het uur in — neem contact op via wpts.nl.

Wat brute-force op WordPress eruitziet

Open je access.log (te vinden in cPanel/Plesk onder "Raw access logs") en zoek naar POST-requests op wp-login.php. Op een willekeurige WordPress-site zie je iets dat hierop lijkt:

185.220.101.42 - - [04/May/2026:12:00:01] "POST /wp-login.php HTTP/1.1" 200 4521
193.106.196.10 - - [04/May/2026:12:00:02] "POST /wp-login.php HTTP/1.1" 200 4521
45.155.205.235 - - [04/May/2026:12:00:03] "POST /wp-login.php HTTP/1.1" 200 4521
185.220.101.45 - - [04/May/2026:12:00:04] "POST /wp-login.php HTTP/1.1" 200 4521
... (honderden regels per dag)

Verschillende IP-adressen, allemaal POSTs naar wp-login. Vrijwel altijd zijn dit botnets die woordenboeken aflopen op admin-account-namen als admin, administrator, jouwdomeinnaam, of de bekende wp-username.

Waarom is dit erg?

Drie redenen, in volgorde van waarschijnlijkheid:

  1. Server-belasting: elke POST naar wp-login.php triggert een volledige WordPress-pageload (DB-query om user te checken, hash-vergelijking, sessie-init). Bij honderden requests per minuut raakt je shared hosting CPU-quota op, of bij een eigen VPS swappen je PHP workers naar disk.
  2. Cascade naar 503-errors: PHP-FPM workers staan te wachten op DB-queries, leveranciers leveren niet door — je echte bezoekers krijgen een 503 Service Unavailable. Site is feitelijk offline door de aanval.
  3. Eventueel geslaagde inbraak: als je een zwak wachtwoord hebt en geen 2FA, is het een kwestie van tijd. Een typisch botnet probeert 100.000+ combinaties.

Wordfence en vergelijkbare plugins lossen dit op door zelf op elke pageload te draaien, eigen rules te checken, IPs te blokkeren in een eigen tabel, en een dashboard met statistieken op te bouwen. Effect: je site is veiliger, maar ook 30-50% trager. Voor een goedlopende site een onnodige trade-off.

Methode 1 — Cloudflare Rate Limiting (krachtigste, gratis)

Als je site achter Cloudflare staat (gratis plan is voldoende), kun je een rate-limit-regel maken die brute-force verzoeken al op Cloudflare-niveau blokkeert — voordat ze ook maar je server raken.

Setup

  1. Login op Cloudflare → kies je site
  2. Security → WAF → Rate limiting rules
  3. Klik Create rule

Configuratie:

Rule name: Block brute force on wp-login

If incoming requests match:
    URI Path  equals  /wp-login.php
    AND Request Method  equals  POST

Then take action: Block
For duration: 1 hour
When rate exceeds: 5 requests per 1 minute
Counting characteristics: IP address

Sla op. Vanaf nu wordt elk IP dat meer dan 5 keer per minuut POST naar wp-login.php geblokkeerd voor een uur. Geen impact op je echte bezoekers (die loggen niet 5x per minuut in), wel directe stop op botnets.

Bonus — XML-RPC ook rate-limiten

Maak een tweede rule voor /xmlrpc.php met dezelfde drempel. XML-RPC is een veel populairder doel dan wp-login omdat je 100 wachtwoord-pogingen in één request kan doen via system.multicall.

Het Free-plan van Cloudflare staat 1 rate-limiting-regel toe; Pro (€20/maand) gaat naar 5. Voor één site met enkel wp-login + xmlrpc-bescherming heb je Pro nodig — maar je kunt ook beide paden in één regel combineren met een OR-conditie.

Methode 2 — .htaccess IP-whitelist op wp-login.php

De effectiefste bescherming: alleen jij kunt bij wp-login.php. Vanaf elk ander IP wordt de pagina geblokkeerd. Werkt zonder plugin, zonder Cloudflare, alleen Apache.

Setup

Voeg toe aan je .htaccess in de WordPress-root:

# Bescherm wp-login.php tegen onbekende IP's
<Files wp-login.php>
    Require all denied
    Require ip 123.45.67.89
    Require ip 98.76.54.32
</Files>

# Blokkeer xmlrpc.php compleet (zelden nodig in 2026)
<Files xmlrpc.php>
    Require all denied
</Files>

Vervang de IPs door jouw eigen vaste IP (en bijvoorbeeld dat van je kantoor of VPN). Vind je IP op whatismyip.com.

Voordeel: 100% effectief — botnets met willekeurige IPs raken nooit door, krijgen direct een 403.

Nadeel: je moet altijd vanaf hetzelfde IP komen om in te kunnen loggen. Heb je een dynamisch IP (KPN/Ziggo woon-internet wisselt af en toe), dan zit je een dag zonder admin-toegang.

Variant — heel land toelaten in plaats van één IP

Niet ideaal qua security maar pragmatisch: blokkeer alle landen behalve Nederland. Dit gaat alleen via Cloudflare of via mod_geoip op je eigen server. Voor de meeste shared-hostings: gebruik Cloudflare's GeoIP-feature in een Custom Rule:

(http.request.uri.path eq "/wp-login.php" and ip.geoip.country ne "NL")
=> Block

Stopt 95% van botnet-traffic (die komt vrijwel nooit uit NL).

Methode 3 — .htaccess basic-auth (extra wachtwoord-laag)

Werkt overal, ook op dynamische IPs. Bezoekers van wp-login.php moeten eerst een Apache-wachtwoord invullen vóór ze überhaupt de WordPress-login zien.

Setup

Stap 1: maak een wachtwoord-bestand. Via SSH:

htpasswd -c /home/jouwuser/.htpasswd webadmin

Wordt om wachtwoord gevraagd. Vervang webadmin door een gebruikersnaam naar keuze (mag niet hetzelfde zijn als je WordPress-admin-naam — dan ben je niets veiliger).

Geen SSH? Maak een .htpasswd-bestand handmatig met deze online generator: htpasswd.org (genereer hashed wachtwoord, plak in een plat tekstbestand .htpasswd en upload naar een directory die NIET in de webroot zit).

Stap 2: voeg toe aan .htaccess:

<Files wp-login.php>
    AuthType Basic
    AuthName "Restricted"
    AuthUserFile /home/jouwuser/.htpasswd
    Require valid-user
</Files>

# Voor wp-admin map (extra laag voor admin-pagina's)
<FilesMatch "^.*$">
    AuthType Basic
    AuthName "Restricted"
    AuthUserFile /home/jouwuser/.htpasswd
    Require valid-user
</FilesMatch>

Tweede blok plaats je in /wp-admin/.htaccess (apart bestand) — dat beschermt de hele admin-map.

Pas op: bepaalde plugins en WP zelf maken AJAX-calls naar wp-admin/admin-ajax.php vanaf de frontend. Voeg uitzondering toe:

# In /wp-admin/.htaccess — ná de auth-regels
<Files admin-ajax.php>
    Order allow,deny
    Allow from all
    Satisfy any
</Files>

Anders breken plugins die AJAX gebruiken (contact-formulieren, page builders, sommige WooCommerce-features).

Methode 4 — XML-RPC fundamenteel uitschakelen

XML-RPC is een API uit 2003 voor remote-publicatie van WordPress posts. In 2026 gebruiken < 1% van de sites dit nog actief — meestal alleen Jetpack, oude WordPress mobile apps, of een paar self-hosted publishing-tools.

Test eerst of je het gebruikt

In je access.log (filter op je eigen IP):

grep "xmlrpc" /var/log/access.log | grep "JOUW_IP"

Geen treffers? Veilig om uit te zetten. Wel treffers? Check welke applicatie het gebruikt voor je het uitschakelt.

Drie manieren om uit te schakelen

1. Via .htaccess (snelste):

<Files xmlrpc.php>
    Require all denied
</Files>

2. Via WordPress filter in functions.php (van je kindthema!):

add_filter('xmlrpc_enabled', '__return_false');

3. Cloudflare Custom Rule:

(http.request.uri.path eq "/xmlrpc.php")
=> Block

Methode 1 (.htaccess) is het strikst — XML-RPC is dan ook niet bereikbaar via een omweg. Methode 2 (WP filter) laat de URL nog wel bestaan maar disabled de functionaliteit, wat brute-forcers nog steeds CPU laat gebruiken om de check te doen.

Lichtgewicht plugin-alternatief: Limit Login Attempts Reloaded

Als je géén Cloudflare hebt en .htaccess-aanpassingen lastig zijn (bijv. op managed-hosting waar je geen .htaccess kunt bewerken), is er één plugin die wel acceptabel is qua resource-gebruik:

Limit Login Attempts Reloaded (gratis):

  • ~3 MB extra geheugen per pageload (vs. 200-500 MB voor Wordfence)
  • Doet één ding goed: telt foute logins per IP, blokkeert tijdelijk na N pogingen
  • Geen scan-functies, geen WAF, geen log-archief — minimal footprint
  • Configureerbaar: 4 pogingen → 20 minuten lockout, 4 lockouts → 24 uur

Dit is wat 95% van WP-sites écht nodig heeft. De rest van Wordfence' features (file-scanning, malware-detect, country-blocking) doe je beter op andere lagen — Cloudflare voor country-blocking, je hosting voor file-monitoring, en je eigen backup-strategie voor recovery.

Detectie: hoe weet je of je aangevallen wordt?

Op shared hosting via Raw access logs (cPanel/Plesk):

# Aantal POSTs naar wp-login per IP, gesorteerd
grep "POST /wp-login" access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

# Aantal POSTs naar xmlrpc per IP
grep "POST /xmlrpc" access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

Output:

   1247 185.220.101.42
    893 193.106.196.10
    421 45.155.205.235
    387 91.219.236.149
     12 84.106.123.45      <- jouw IP, normale activiteit

Bij 100+ pogingen vanuit één IP is het 100% een bot. Bij > 1000 over meerdere IPs ben je actief target.

Wachtwoord-hygiene als laatste verdedigingslinie

Zelfs zonder enige bovenstaande maatregel — een sterk wachtwoord stopt 99% van brute-force-pogingen. Botnets proberen admin/admin, admin/password, admin/123456, etc. Een 16+ tekens random wachtwoord wordt nooit geraden binnen menselijke tijdsperiodes.

Vier basis-regels:

  1. Geen "admin" of de domeinnaam als gebruikersnaam. Ook geen je-eigen-naam als die op de site staat.
  2. 16+ tekens, gegenereerd door wachtwoord-manager. Mensen-bedachte wachtwoorden zijn altijd zwakker dan ze denken.
  3. Twee-factor-authenticatie aan. Optie via plugins zoals "Two Factor" (gratis, gemaakt door WP-core developers, ~5MB footprint).
  4. Verschillende wachtwoorden per site. Eén lek via een hack van site A betekent dat site B+C+D ook potential gevaar lopen als je hetzelfde wachtwoord gebruikt.

Eindconfiguratie — wat ik bij klanten installeer

Het meest gangbare patroon:

  1. Cloudflare (gratis tier) als CDN + reverse proxy
  2. Cloudflare Rate Limiting rule op wp-login.php (5 POSTs/min per IP)
  3. Cloudflare Custom Rule die alle landen behalve NL/BE blokkeert op wp-login.php
  4. .htaccess met <Files xmlrpc.php> Require all denied </Files>
  5. Limit Login Attempts Reloaded plugin als laatste schil (4-pogingen → 20-min-lockout)
  6. Two Factor plugin voor admin-account met TOTP-app
  7. Sterke wachtwoorden in een wachtwoord-manager

Totale extra geheugen-impact: ~10 MB. Vergelijk dat met 250-500 MB voor Wordfence. En de bescherming is gelijkwaardig of sterker — omdat de meeste lagen al op Cloudflare-niveau weren, zelfs voordat je server het ziet.

Wanneer is Wordfence wél de moeite?

Eerlijk: er zijn scenario's waarin Wordfence (of Solid Security, Sucuri) wel zinvol is:

  • Je hebt geen Cloudflare en kunt geen .htaccess bewerken (managed/cloud hostings)
  • Je beheert 50+ WordPress-sites en wilt one-click-management van security-status
  • Je hebt regelmatig file-changes die je geautomatiseerd wilt monitoren — Wordfence' file-integrity-check is solide
  • Je wilt direct e-mailmeldingen bij verdachte activiteit zonder zelf logs te scrapen

Voor 90% van NL MKB-sites met één website op shared hosting met Cloudflare ervoor — niet nodig.

Snelle test: werkt mijn bescherming?

Na inzet, test:

  1. Open een incognito-venster
  2. Probeer 6× achter elkaar in te loggen op /wp-login.php met een fout wachtwoord
  3. Bij Limit Login: zie je "Too many failed attempts" — werkt
  4. Bij Cloudflare: zie je een Cloudflare 1015 of 429 — werkt
  5. Bij .htaccess IP-whitelist: zie je Apache 403 vanaf een ander IP (gebruik VPN om vanaf andere locatie te testen) — werkt

Test óók of je echte bezoekers niet per ongeluk ge-blokkeerd worden. Doe in een incognito een normale paginabezoek — die moet snel laden.

Ben je nu al onder aanval?

Als je merkt dat je site nu langzamer is dan normaal, of 503-errors geeft op piek-momenten, en je access.log laat duizenden POSTs naar wp-login zien — sluit eerst de aanval af voor je over preventie nadenkt. Cloudflare's "Under Attack Mode" zet een tijdelijk JavaScript-challenge tussen elke bezoeker en je site, wat botnets effectief stopt zonder echte bezoekers te frustreren. Dat geeft je tijd om een van bovenstaande methoden permanent in te richten.