Home › Blog › Article

Hardening WordPress: Login, Permissions and File Monitoring

Hardening WordPress: Login, Permissions and File Monitoring

Apply login throttling and two-factor auth, run PHP as a non-root user with 644/755 permissions, set DISALLOW_FILE_EDIT to true, strip version meta only after weighing trade-offs, and ship auth/file logs off-site. You will know it worked when failed logins are blocked, no editor appears in wp-admin, file writes outside deploy paths alert you, and wp core verify-checksums returns clean.

Why WordPress Security Hardening Matters in 2026

Attacks in 2026 still target the same weak points: stolen credentials, over-privileged processes, and silent file changes. WordPress security hardening is not one plugin; it is a repeatable set of controls you can verify. If you lack in-house capacity, our team handles this through WordPress Customization & Optimization, and we document the same controls in our technical column index. Pair these steps with a restore plan such as the one in WordPress Backup Strategy: 3-2-1 and Restoring so a mistake is recoverable.

Step-by-Step Hardening Process for Login, Permissions and Files

  1. Enforce MFA and rate limiting on wp-login.php at the edge or plugin layer.
  2. Reconfigure PHP-FPM pool and file ownership to a least-privilege user.
  3. Set DISALLOW_FILE_EDIT and lock down wp-config.php permissions.
  4. Remove or keep version meta deliberately, then verify with curl.
  5. Forward auth and file-integrity logs off-site and alert on diffs.

Login Protection

Brute force and credential stuffing remain the top entry vector. Protect wp-login.php and /wp-admin/ before an attacker reaches the database. Use a plugin such as Wordfence or a server rule; do not rely on obscurity alone.

Rate limit with nginx

Add a limit zone and apply it to login requests. This returns 429 after repeated failures from one IP.

limit_req_zone $binary_remote_addr zone=wplogin:10m rate=5r/m;
server {
    location = /wp-login.php {
        limit_req zone=wplogin burst=3 nodelay;
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
}

Verify: curl -I https://example.com/wp-login.php six times quickly; the sixth should return HTTP 429 or 503 depending on config. Rollback: comment the limit_req line and reload nginx with nginx -t && systemctl reload nginx. Pitfall: putting the limit in the wrong location block misses the request; always match the exact login path.

Require two-factor authentication

Install Two-Factor (wordpress.org/plugins/two-factor) or an equivalent. Enforce it for administrator and editor roles. Verify by logging in as an admin: you must supply a TOTP code. Rollback: deactivate the plugin if lockout occurs, then re-enable after fixing the enrollment. Pitfall: losing the TOTP secret without backup codes locks you out; store recovery codes offline.

Least Privilege

Get a Quote

Email + requirement + budget range — we reply with a quote within 24 hours.

Run PHP as a dedicated user, not root, and give that user only the files it must write. This contains a webshell to wp-content/uploads instead of the whole filesystem.

Set ownership and permissions

Assume deploy user deploy and PHP user www-data on Ubuntu 24.04 with PHP 8.3 in 2026:

chown -R deploy:www-data /var/www/example.com
find /var/www/example.com -type d -exec chmod 755 {} ;
find /var/www/example.com -type f -exec chmod 644 {} ;
chmod 640 /var/www/example.com/wp-config.php
chown deploy:www-data /var/www/example.com/wp-config.php

Verify: namei -l /var/www/example.com/wp-config.php shows mode 640 owned by deploy:www-data, and ls -ld wp-content/uploads is writable only by www-data. Rollback: restore previous modes from your backup or chmod 664 temporarily if an upload breaks, then re-tighten. Pitfall: 777 anywhere is a finding; never use it.

Lock the PHP-FPM pool

In /etc/php/8.3/fpm/pool.d/www.conf set user = www-data and group = www-data, never root. Verify with ps aux | grep php-fpm: workers run as www-data. Rollback: revert the file and systemctl restart php8.3-fpm.

Disabling the File Editor

The built-in theme and plugin editor lets anyone with admin rights execute PHP from the browser. Turn it off so a compromised admin session cannot edit code.

Set the constant

Add to wp-config.php above the /* That's all */ line:

define( 'DISALLOW_FILE_EDIT', true );
define( 'DISALLOW_FILE_MODS', true );

Verify: log in as admin and open Appearance; the Theme Editor link must be gone. Also check Plugins; no Add New should appear if DISALLOW_FILE_MODS is set. Rollback: set the value to false or remove the line if you must install from wp-admin, then restore after. Pitfall: DISALLOW_FILE_MODS also blocks updates from the dashboard; use WP-CLI instead: wp plugin update --all.

Hiding Version Info Trade-offs

Hiding Version Info Trade-offs

WordPress prints its version in the <meta name='generator'> tag and in readme files. Removing it slows casual scanners, but it is not real access control and can break support checks.

Remove the generator tag

In your child theme functions.php:

remove_action( 'wp_head', 'wp_generator' );
add_filter( 'the_generator', '__return_empty_string' );

Verify: curl -s https://example.com | grep -i generator returns nothing. Trade-off: attackers can still fingerprint via /readme.html and plugin paths, so also block those:

location = /readme.html { return 404; }

Rollback: delete the filter if a support tool needs the version, or temporarily allow readme. Pitfall: hiding version without patching gives false comfort; update core to 6.8 or current 2026 release instead of relying on obscurity. See WordPress security documentation for supported practices.

Log Monitoring

You cannot stop every attempt, but you can see them. Monitor authentication failures and file integrity, then ship logs off the server so an attacker cannot erase them.

Check core integrity

Run weekly from cron as the deploy user:

wp core verify-checksums
wp plugin verify-checksums --all

Verify: output says Success: WordPress installation verifies against checksums. A diff means tampering; restore from backup. Rollback: none needed; this is read-only.

Forward and alert on logs

Send nginx and PHP auth logs to a remote syslog or SIEM. Example rsyslog filter for failed logins:

if $programname == 'nginx' and $msg contains 'wp-login.php' and $msg contains '429' then @@logs.example.net:514

Verify: trigger a blocked login and confirm the event lands off-site. Pitfall: local-only logs are useless after compromise; always forward. Combine with file watching via auditd on wp-content for unexpected writes.

Action Checklist

  • Rate limit wp-login.php and require MFA for admins.
  • Set files to 644/755, wp-config.php to 640, PHP-FPM to non-root.
  • Define DISALLOW_FILE_EDIT and confirm the editor is gone.
  • Strip the generator tag and block /readme.html.
  • Run wp core verify-checksums and forward logs off-site.

Need this implemented and verified on your stack? Our engineers harden logins, permissions and monitoring for production WordPress. See our WordPress Customization & Optimization service to get started today.

Frequently asked questions

Does hiding the WordPress version actually stop attacks?

No. It removes low-effort scanning signals, but you must still patch core and plugins; treat it as a minor layer, not a control.

What permission should wp-config.php have in 2026?

Use 640 owned by your deploy user and the PHP group. This blocks world reads while letting PHP load it; never leave it at 644 or 666.

Can I re-enable the file editor temporarily?

Yes. Set DISALLOW_FILE_EDIT to false, make the change, then set it back to true and verify the editor link is absent again.

References & further reading

Need WordPress Development?

Free proposal and quote within 24 hours — we serve clients worldwide.

QQ