Scope
Applies to any production WordPress site, with extra emphasis on
agency hosting (multiple client sites on shared infrastructure).
Operational detail for each item lives in
wordpress-hardening.
WordPress sites almost always process personal data — commenters, contact-form submissions, customer accounts. GDPR applies; treat the host as in-scope.
1. Pre-build
- VPS baseline complete. Work through the pre-launch VPS checklist before installing WordPress.
- Privacy decisions made. See
privacy-by-design-server-buildfor the design-level questions — they apply to WordPress too.
2. Server-level isolation
- Per-site Unix user. Each client site owns its own user,
group, document root. No shared
www-data. NIS2: 21(2)(e); ISO 27001: A.5.15. - Per-site PHP-FPM pool. Separate socket, separate
open_basedir, separate worker limits. -
open_basedirscoped to the site’s root. Prevents cross-site filesystem access if one site is compromised. -
disable_functionsconfigured.exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_sourcedisabled at minimum. -
expose_php = Off.
3. Filesystem permissions
- Source code is not web-writable. PHP-FPM has read, no write
on
wp-includes,wp-admin, plugin and theme directories. NIS2: 21(2)(e); ISO 27001: A.8.4. -
wp-config.phpmode640, groupnginx(or your web group). Never world-readable. - PHP execution blocked under
uploads/. Nginxdeny allfor\.phprequests under the uploads tree. -
.git,.env, backups not in the document root. Verified by attempting to fetch them via HTTP. -
xmlrpc.phpblocked. Unless a documented use case requires it; in which case IP-allowlist.
4. WordPress core configuration
-
FORCE_SSL_ADMIN = trueinwp-config.php. -
DISALLOW_FILE_EDIT = true. No theme/plugin editor in the dashboard. -
DISALLOW_FILE_MODS = true. Updates happen viawp-cli, not in-dashboard. -
FS_METHOD = 'direct'. Required when the web user cannot write source files. - Authentication salts regenerated. Unique per install, rotated after any suspected compromise. NIS2: 21(2)(h); ISO 27001: A.8.24.
- XML-RPC disabled at the WordPress layer too.
add_filter('xmlrpc_enabled', '__return_false');.
5. Database
- Dedicated database user per site. Not shared across sites.
- Least-privilege grants only.
SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX,ALTER,CREATE TEMPORARY TABLES,LOCK TABLES. No global grants. NIS2: 21(2)(e); ISO 27001: A.5.15. - Database baseline applied. See
postgresql-hardeningfor Postgres, or apply equivalent MariaDB/MySQL hardening. - TLS between WordPress and database. Mandatory across hosts.
6. Admin access
- No
adminusername. Removed at install; per-human admin accounts only. NIS2: 21(2)(i); ISO 27001: A.5.16. - Strong password policy enforced. Minimum length, no common passwords.
- 2FA required for every admin account. Hardware key or TOTP preferred over SMS. NIS2: 21(2)(j); ISO 27001: A.8.5.
- Login rate limiting active. Nginx
limit_reqonwp-login.php(seenginx-ratelimit) plus a plugin like Limit Login Attempts Reloaded. -
/wp-adminIP-allowlisted where feasible. If client admins work from known networks.
7. Plugins and themes
- Source vetted. Every plugin from
wordpress.orgor a reputable vendor, updated within six months, not in a “closed” state. NIS2: 21(2)(d) (supply chain). - Premium plugins licensed and current. No nulled / pirated copies — common compromise vector.
- Inactive plugins removed. Inactive ≠ uninstalled. Uninstall what is not in use.
- Update SLA documented. Security-flagged updates within 24 hours; routine updates within 7 days. NIS2: 21(2)(e),(g).
- No in-dashboard file editing. Enforced by
DISALLOW_FILE_EDITand by policy.
8. Backups
- Daily database backup + weekly content backup scheduled.
- Off-site, encrypted backup target. restic / borg to EEA region. GDPR: Article 32(1)(c).
- Backup retention matches the privacy policy. Cannot exceed stated user-data retention. GDPR: Article 5(1)(e).
- Backups not in the document root. Common misconfiguration — verify by HTTP.
- Restoration drill performed and timed. Quarterly minimum.
9. Logging and integrity
- Nginx access logs use a minimised
log_format. Per the privacy-by-design guide. - Login attempts logged and alerted on.
-
wp core verify-checksumsscheduled and alerting. NIS2: 21(2)(g); ISO 27001: A.8.16. -
wp plugin verify-checksums --allscheduled.
10. GDPR-specific items
- Privacy notice published and accurate. Reflects what is collected, including by plugins.
- Cookie consent implemented for any non-essential cookies. No pre-ticked boxes. GDPR: Article 7.
- Right to erasure tested. WordPress core supports it; verify every plugin storing personal data respects the request.
- Right to access tested. Data export produces a complete and readable file.
- Sub-processor list maintained. Every plugin or service that sends data off-site (analytics, email, payment, CRM). GDPR: Article 28.
11. Ongoing operation
- Update process documented. Who runs it, when, how out-of-hours updates are handled.
- Quarterly re-run of this checklist. Plugin drift, retention drift, and admin-account creep are the usual findings.
- External vulnerability scan run. WPScan or equivalent; results documented.
Notes
For agency hosting at scale, every item under “Server-level isolation” is non-negotiable. One compromised site can pivot to siblings within minutes if isolation is missing — and the compounding blast radius is exactly the failure mode regulators ask about first.
If the same baseline is being applied to dozens of sites, treat this list as the content and a configuration management tool — Ansible, WP-CLI scripts, or a WordPress-aware orchestrator — as the enforcement.