---
title: "Secure WordPress After a Hack: 12 Essential Measures"
description: "12 measures to secure WordPress after a hack. WAF firewall, 2FA, file permissions, HTTP headers and monitoring setup."
date: "2026-03-11"
author: "Bastien Allain"
category: "SEO"
tags: ["wordpress", "security", "hacking", "hardening", "waf", "2fa"]
keywords: ["wordpress", "security", "hacking", "hardening", "waf", "2fa"]
image: "/blog/category-seo.webp"
published: true
locale: "en"
translationKey: "secure-wordpress-after-hack"
imageAlt: "Secure WordPress After a Hack: 12 Essential Measures"
lastmod: "2026-03-11"
authorImage: "/authors/bastien-allain.webp"
authorTwitter: ""
isFamilyFriendly: true
---

A WordPress hack is never trivial. Even after a successful cleanup, your site remains vulnerable if you do not drastically strengthen its security. The statistics speak for themselves: **73% of cleaned WordPress sites** get re-infected within 30 days due to inadequate hardening measures. If you have not cleaned your site yet, start with our [complete cleanup guide in 10 steps](/blog/seo/wordpress-hacked-cleanup-guide). This guide presents **12 essential measures** to secure WordPress after a hack, covering every layer of protection from the server to the admin interface. Each measure includes the specific commands and configurations to implement.

## 1. Change All Passwords

The first measure after a hack is to consider that **all credentials are compromised**. Systematically change every password connected to your WordPress environment.

### Passwords to Change

| Account | Where to change | Priority |
|---------|----------------|----------|
| **WordPress admin accounts** | WP-CLI or dashboard | Critical |
| **MySQL database** | phpMyAdmin or command line | Critical |
| **FTP / SFTP** | Hosting panel | Critical |
| **SSH** | SSH key or hosting panel | Critical |
| **Hosting panel** (cPanel, Plesk) | Hosting interface | High |
| **Associated email accounts** | Email provider | High |
| **CDN and third-party services** | Service dashboard | Medium |

### WP-CLI Commands

```bash
# Change password for all administrators
wp user list --role=administrator --field=ID --path=/var/www/html | while read id; do
  NEW_PASS=$(openssl rand -base64 24)
  wp user update $id --user_pass="$NEW_PASS" --path=/var/www/html
  echo "User $id: password updated"
done
```

### Change Database Credentials

```bash
# 1. Change MySQL password
mysql -u root -p -e "ALTER USER 'wp_user'@'localhost' IDENTIFIED BY 'NewComplexPassword_2026!';"

# 2. Update wp-config.php with the new password
wp config set DB_PASSWORD 'NewComplexPassword_2026!' --path=/var/www/html
```

Use passwords of at least **20 characters** with a mix of uppercase, lowercase, numbers and special characters.

## 2. Regenerate WordPress Security Keys

WordPress **security keys** (salts) are used to encrypt cookies and sessions. If an attacker knows them, they can forge valid sessions even after password changes.

```bash
# Automatically regenerate all keys
wp config shuffle-salts --path=/var/www/html
```

This command replaces the 8 security constants in `wp-config.php`:

- `AUTH_KEY`
- `SECURE_AUTH_KEY`
- `LOGGED_IN_KEY`
- `NONCE_KEY`
- `AUTH_SALT`
- `SECURE_AUTH_SALT`
- `LOGGED_IN_SALT`
- `NONCE_SALT`

Regenerating keys **immediately disconnects all active sessions**, including any potentially used by the attacker.

## 3. Install a WAF (Web Application Firewall)

A **WAF** filters HTTP traffic before it reaches WordPress, blocking malicious requests upstream.

### WAF Options

| Solution | Type | Advantages |
|----------|------|------------|
| **Cloudflare** | Cloud (reverse proxy) | DDoS protection included, CDN, free tier |
| **Sucuri Firewall** | Cloud | WordPress specialized, cleanup included |
| **Wordfence** | Application plugin | Deep inspection, free tier |
| **ModSecurity** | Server (Apache/Nginx module) | Performance, OWASP rules |

### Cloudflare Configuration (Recommended)

1. Create a Cloudflare account and add your domain
2. Change nameservers at your registrar
3. Enable WordPress security rules:

```
Cloudflare Dashboard > Security > WAF > Managed Rules
- Enable "Cloudflare WordPress Ruleset"
- Enable "Cloudflare OWASP Core Ruleset"
```

### ModSecurity Configuration (Server)

```bash
# Install ModSecurity on Apache
sudo apt install libapache2-mod-security2
sudo a2enmod security2

# Download OWASP CRS rules
cd /etc/modsecurity/
sudo git clone https://github.com/coreruleset/coreruleset.git
sudo cp coreruleset/crs-setup.conf.example coreruleset/crs-setup.conf

# Enable rules in Apache configuration
sudo cat >> /etc/apache2/mods-enabled/security2.conf << 'EOF'
IncludeOptional /etc/modsecurity/coreruleset/crs-setup.conf
IncludeOptional /etc/modsecurity/coreruleset/rules/*.conf
EOF

sudo systemctl restart apache2
```

## 4. Enable Two-Factor Authentication (2FA)

**2FA** adds an extra security layer by requiring a temporary code in addition to the password. Even if an attacker obtains credentials, they cannot log in without the second factor.

### Installation with WP-CLI

```bash
# Install the Two-Factor plugin
wp plugin install two-factor --activate --path=/var/www/html
```

### Recommended Configuration

1. Enable **TOTP** (Time-based One-Time Password) as the primary method
2. Configure **backup codes** in case of phone loss
3. **Force 2FA for all administrators and editors**

### Programmatic Configuration (functions.php or mu-plugin)

```php
<?php
// Force 2FA for administrators
add_filter('two_factor_providers', function($providers) {
    // Keep only TOTP and backup codes
    return array(
        'Two_Factor_Totp'          => $providers['Two_Factor_Totp'],
        'Two_Factor_Backup_Codes'  => $providers['Two_Factor_Backup_Codes'],
    );
});
```

For optimal security, prefer an **authenticator app** (Google Authenticator, Authy, 1Password) over SMS, which is vulnerable to SIM swapping.

## 5. Configure File Permissions

**Incorrect file permissions** are one of the most common vulnerabilities and make it easier for attackers to inject [infected files](/blog/seo/infected-wordpress-files) onto your server. Here are the recommended permissions for WordPress:

### Recommended Permissions

| Element | Permission | Command |
|---------|-----------|---------|
| Directories | 755 | `find . -type d -exec chmod 755 {} \;` |
| Files | 644 | `find . -type f -exec chmod 644 {} \;` |
| wp-config.php | 400 or 440 | `chmod 400 wp-config.php` |
| .htaccess | 444 | `chmod 444 .htaccess` |

### Apply Correct Permissions

```bash
cd /var/www/html

# Directory permissions
find . -type d -exec chmod 755 {} \;

# File permissions
find . -type f -exec chmod 644 {} \;

# Restrictive permissions for wp-config.php
chmod 400 wp-config.php

# Restrictive permissions for .htaccess
chmod 444 .htaccess

# Correct ownership (replace www-data with your web user)
chown -R www-data:www-data /var/www/html/
```

### Prevent PHP File Creation in Uploads

Add an `.htaccess` file in the `wp-content/uploads/` directory:

```apache
# wp-content/uploads/.htaccess
<FilesMatch "\.(?:php|phtml|php5|php7|phps)$">
  Order Allow,Deny
  Deny from all
</FilesMatch>
```

For Nginx:

```nginx
location ~* /wp-content/uploads/.*\.php$ {
    deny all;
}
```

## 6. Disable the Built-in File Editor

The WordPress built-in file editor allows modifying theme and plugin PHP files directly from the dashboard. This is a **major entry point** for attackers who gain admin access.

```bash
# Add to wp-config.php
wp config set DISALLOW_FILE_EDIT true --raw --path=/var/www/html
```

This constant completely disables the theme and plugin editor in the dashboard. For even stricter security, also disable plugin and theme installation from the dashboard:

```bash
# Prevent plugin/theme installation and updates from the dashboard
wp config set DISALLOW_FILE_MODS true --raw --path=/var/www/html
```

With `DISALLOW_FILE_MODS`, updates must be performed **exclusively via WP-CLI or SFTP**.

## 7. Protect wp-config.php

The `wp-config.php` file contains the most sensitive information on your site: database credentials, security keys and site configuration.

### Protection via .htaccess (Apache)

```apache
# Prevent direct access to wp-config.php
<Files wp-config.php>
  Order Allow,Deny
  Deny from all
</Files>
```

### Protection via Nginx

```nginx
location ~ /wp-config\.php$ {
    deny all;
}
```

### Move wp-config.php

WordPress allows placing `wp-config.php` one level above the web root:

```bash
# Move wp-config.php above the web root
mv /var/www/html/wp-config.php /var/www/wp-config.php
```

WordPress automatically detects the file one level up. This technique adds a layer of protection by placing the file **outside the publicly accessible directory**.

### Add Security Constants

```bash
# Force HTTPS
wp config set FORCE_SSL_ADMIN true --raw --path=/var/www/html

# Disable debug in production
wp config set WP_DEBUG false --raw --path=/var/www/html
wp config set WP_DEBUG_DISPLAY false --raw --path=/var/www/html
wp config set WP_DEBUG_LOG false --raw --path=/var/www/html
```

## 8. Secure the .htaccess File

The `.htaccess` file controls Apache server behavior. A properly configured `.htaccess` significantly strengthens security.

### Recommended Security Configuration

```apache
# Protect .htaccess itself
<Files .htaccess>
  Order Allow,Deny
  Deny from all
</Files>

# Prevent directory listing
Options -Indexes

# Block access to sensitive files
<FilesMatch "(^\.ht|wp-config\.php|readme\.html|license\.txt|xmlrpc\.php)">
  Order Allow,Deny
  Deny from all
</FilesMatch>

# Block PHP execution in wp-includes
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^wp-admin/includes/ - [F,L]
  RewriteRule !^wp-includes/ - [S=3]
  RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
  RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
  RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>

# Block PHP execution in wp-includes (alternative)
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{SCRIPT_FILENAME} !^(.*)wp-includes/ms-files.php
  RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
</IfModule>

# Block suspicious query strings
<IfModule mod_rewrite.c>
  RewriteCond %{QUERY_STRING} (\<|%3C).*script.*(\>|%3E) [NC,OR]
  RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
  RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
  RewriteRule ^(.*)$ index.php [F,L]
</IfModule>
```

## 9. Disable XML-RPC

**XML-RPC** is a legacy communication protocol for WordPress. It is rarely used but represents a major attack vector for **brute force attacks** and **amplified DDoS attacks**.

### Disable via .htaccess

```apache
# Completely block access to xmlrpc.php
<Files xmlrpc.php>
  Order Allow,Deny
  Deny from all
</Files>
```

### Disable via a WordPress Filter

```php
<?php
// Add to functions.php or a mu-plugin
add_filter('xmlrpc_enabled', '__return_false');

// Remove the X-Pingback header
add_filter('wp_headers', function($headers) {
    unset($headers['X-Pingback']);
    return $headers;
});

// Disable all XML-RPC methods
add_filter('xmlrpc_methods', function($methods) {
    return array();
});
```

### Disable via Nginx

```nginx
location = /xmlrpc.php {
    deny all;
    access_log off;
    log_not_found off;
}
```

If you use the WordPress mobile app or Jetpack, you will need to keep XML-RPC active. In that case, limit access to specific IP addresses only.

## 10. Limit Login Attempts

**Brute force attacks** target the WordPress login page (`wp-login.php`). Limiting login attempts blocks these attacks effectively.

### Install Limit Login Attempts Plugin

```bash
# Install and activate the plugin
wp plugin install limit-login-attempts-reloaded --activate --path=/var/www/html
```

### Recommended Configuration

| Parameter | Recommended value |
|-----------|------------------|
| **Allowed attempts** | 3 |
| **Lockout duration** | 20 minutes |
| **Lockouts before extended block** | 3 |
| **Extended block duration** | 24 hours |
| **Email notification** | After 2 lockouts |

### Additional Protection via .htaccess

```apache
# Limit wp-login.php access by IP address
<Files wp-login.php>
  Order Deny,Allow
  Deny from all
  # Allow your IP (replace with your IP)
  Allow from 203.0.113.42
  Allow from 198.51.100.0/24
</Files>
```

### Change the Login URL

Modifying the login page URL significantly reduces automated attacks:

```bash
# Install WPS Hide Login
wp plugin install wps-hide-login --activate --path=/var/www/html

# Configure the new login URL
wp option update whl_page 'my-secure-access' --path=/var/www/html
```

The login page will then be accessible at `your-site.com/my-secure-access` instead of `your-site.com/wp-login.php`.

## 11. Configure HTTP Security Headers

**HTTP security headers** protect against XSS attacks, clickjacking, MIME sniffing and other browser-side threats.

### Recommended Headers

| Header | Function | Value |
|--------|----------|-------|
| **Content-Security-Policy** | Prevents XSS injections | Defines allowed sources |
| **X-Content-Type-Options** | Blocks MIME sniffing | `nosniff` |
| **X-Frame-Options** | Prevents clickjacking | `SAMEORIGIN` |
| **Strict-Transport-Security** | Forces HTTPS | `max-age=31536000` |
| **Referrer-Policy** | Controls referrer information | `strict-origin-when-cross-origin` |
| **Permissions-Policy** | Restricts browser APIs | As needed |

### Apache Configuration (.htaccess)

```apache
<IfModule mod_headers.c>
  # Force HTTPS for 1 year
  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

  # Prevent clickjacking
  Header always set X-Frame-Options "SAMEORIGIN"

  # Prevent MIME sniffing
  Header always set X-Content-Type-Options "nosniff"

  # Referrer policy
  Header always set Referrer-Policy "strict-origin-when-cross-origin"

  # Permissions policy
  Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()"

  # Content Security Policy (adjust for your site)
  Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://www.google-analytics.com;"
</IfModule>
```

### Nginx Configuration

```nginx
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
```

### Verify Your Headers

After configuration, test your headers with:

```bash
# Check HTTP headers
curl -I https://your-site.com

# Or use an online service like securityheaders.com
```

## 12. Set Up a Monitoring System

The last measure, but arguably the most important in the long run, is to **continuously monitor** your site to detect any anomaly before it becomes a full breach.

### File Integrity Monitoring

```bash
# Create a comprehensive monitoring script
cat > /usr/local/bin/wp-security-monitor.sh << 'SCRIPT'
#!/bin/bash
SITE_PATH="/var/www/html"
LOG="/var/log/wp-security.log"
ALERT_EMAIL="admin@your-site.com"

echo "=== Security scan - $(date) ===" >> $LOG

# 1. Verify core file integrity
echo "[Core Checksums]" >> $LOG
wp core verify-checksums --path=$SITE_PATH >> $LOG 2>&1

# 2. Verify plugin integrity
echo "[Plugin Checksums]" >> $LOG
wp plugin verify-checksums --all --path=$SITE_PATH >> $LOG 2>&1

# 3. PHP files in uploads
echo "[PHP in Uploads]" >> $LOG
PHP_IN_UPLOADS=$(find $SITE_PATH/wp-content/uploads/ -name "*.php" -type f 2>/dev/null)
if [ -n "$PHP_IN_UPLOADS" ]; then
  echo "ALERT: PHP files found in uploads:" >> $LOG
  echo "$PHP_IN_UPLOADS" >> $LOG
  echo "$PHP_IN_UPLOADS" | mail -s "SECURITY ALERT: PHP in uploads" $ALERT_EMAIL
fi

# 4. Files modified in the last 24 hours
echo "[Recently modified files]" >> $LOG
find $SITE_PATH -name "*.php" -mtime -1 -type f >> $LOG 2>&1

# 5. New admin accounts
echo "[Admin accounts]" >> $LOG
wp user list --role=administrator --path=$SITE_PATH --format=table >> $LOG 2>&1

echo "=== End of scan ===" >> $LOG
SCRIPT

chmod +x /usr/local/bin/wp-security-monitor.sh

# Schedule a daily scan
(crontab -l 2>/dev/null; echo "0 4 * * * /usr/local/bin/wp-security-monitor.sh") | crontab -
```

### External Monitoring Services

| Service | Features | Pricing |
|---------|----------|---------|
| **Sucuri SiteCheck** | Malware scan, blacklist, anomalies | Free (basic scan) |
| **UptimeRobot** | Uptime monitoring, real-time alerts | Free (50 monitors) |
| **Wordfence Central** | Multi-site dashboard, scans | Free (basic features) |
| **WPScan** | Plugin/theme vulnerabilities | Free (limited API usage) |

### Google Search Console Alerts

Configure alerts in Google Search Console to be notified immediately if Google detects:

- **Security issues** on your site
- **Hacked content** in the index
- **Manual actions** related to spam

## Summary of All 12 Measures

| Measure | Priority | Difficulty | Impact |
|---------|----------|------------|--------|
| 1. Change all passwords | Critical | Low | Very high |
| 2. Regenerate security keys | Critical | Low | High |
| 3. Install a WAF | High | Medium | Very high |
| 4. Enable 2FA | High | Low | Very high |
| 5. Configure file permissions | High | Low | High |
| 6. Disable file editor | High | Low | Medium |
| 7. Protect wp-config.php | High | Low | High |
| 8. Secure .htaccess | Medium | Medium | High |
| 9. Disable XML-RPC | Medium | Low | Medium |
| 10. Limit login attempts | High | Low | High |
| 11. Configure HTTP headers | Medium | Medium | High |
| 12. Set up monitoring | High | Medium | Very high |

## Automate Hardening with a Script

To apply the most common measures in a single command:

```bash
#!/bin/bash
# WordPress post-hack hardening script
SITE_PATH="/var/www/html"

echo "=== WordPress Hardening ==="

# Regenerate security keys
wp config shuffle-salts --path=$SITE_PATH
echo "[OK] Security keys regenerated"

# Disable file editor
wp config set DISALLOW_FILE_EDIT true --raw --path=$SITE_PATH
echo "[OK] File editor disabled"

# Force HTTPS
wp config set FORCE_SSL_ADMIN true --raw --path=$SITE_PATH
echo "[OK] HTTPS forced for admin"

# Disable debug
wp config set WP_DEBUG false --raw --path=$SITE_PATH
echo "[OK] Debug mode disabled"

# File permissions
find $SITE_PATH -type d -exec chmod 755 {} \;
find $SITE_PATH -type f -exec chmod 644 {} \;
chmod 400 $SITE_PATH/wp-config.php
chmod 444 $SITE_PATH/.htaccess
echo "[OK] File permissions corrected"

# Install and activate security plugins
wp plugin install two-factor --activate --path=$SITE_PATH
wp plugin install limit-login-attempts-reloaded --activate --path=$SITE_PATH
echo "[OK] Security plugins installed"

# Update everything
wp core update --path=$SITE_PATH
wp plugin update --all --path=$SITE_PATH
wp theme update --all --path=$SITE_PATH
echo "[OK] WordPress and extensions updated"

echo "=== Hardening complete ==="
```

## Do Not Leave Your Site Unprotected

WordPress security is an ongoing process, not a one-time action. The 12 measures presented in this guide constitute the **minimum security baseline** after a hack. For optimal protection, combine these measures with **regular security audits** and active monitoring of vulnerabilities in your WordPress ecosystem.

For professional support, discover our [WordPress malware removal service](/services/wordpress/malware-removal) and our [WordPress security solutions](/services/wordpress/security). Also check our [complete post-hack cleanup guide](/blog/seo/wordpress-hacked-cleanup-guide), our catalog of [common WordPress malware in 2026](/blog/seo/common-wordpress-malware-2026), and our [WordPress maintenance guide](/blog/wordpress/maintenance-guide) to prevent relapse.
