WordPress Admin Locked Out: Forensic Diagnosis, Unblocking, and Access Recovery via WP-CLI and MySQL
Back to blog

WordPress Admin Locked Out: Forensic Diagnosis, Unblocking, and Access Recovery via WP-CLI and MySQL

6/7/2026 · 5 min · WordPress

1. Incident scenario and blocking hypotheses#

If the login page (wp-login.php) doesn't even load, your investigation must focus on the following request blocking layers:

  1. Web Server or WAF: HTTP 403 Forbidden errors generated by ModSecurity (Apache) or strict directives in Nginx.
  2. Security Plugin: A block triggered during the WordPress bootstrap by an unstable or overactive security plugin.
  3. Rules in .htaccess: IP blocks/rewrite rules applied by security plugins that persist even after deactivation.
  4. Proxy/CDN Headers: Incorrect client IP detection behind Cloudflare or a reverse proxy.

2. Preventive backups: mandatory P0 step#

Never initiate corrective actions directly in production without first securing backups of your database, plugin files, and server configuration files.

# 1. Full Database backup via WP-CLI
wp db export backup-pre-fix-$(date +%Y%m%d-%H%M%S).sql

# 2. Backup of the content directory (wp-content)
tar czf /tmp/wp-content-backup-$(date +%Y%m%d).tar.gz wp-content/

# 3. Backup of the web server configuration file
cp .htaccess .htaccess.bak.$(date +%F-%H%M%S)

3. Layered diagnosis and log tracing#

3.1 HTTP request test with cURL#

Validate the server's response isolating your local browser session (to bypass local caches):

curl -I https://domain.com/wp-admin/

3.2 temporary debug activation and log tracing#

If the error is generated by WordPress, the bootstrap details will be written to the error log. Temporarily add the following to your wp-config.php:

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);

After adding it, force-reload the page and inspect the relevant log files:

# 1. WordPress debug log
tail -n 50 wp-content/debug.log

# 2. PHP error log (example path on Debian/Ubuntu)
tail -n 50 /var/log/php/error.log

# 3. Web Server error logs
tail -n 50 /var/log/apache2/error.log
tail -n 50 /var/log/nginx/error.log

4. Host files and permissions audit#

4.1 WordPress file and directory permissions#

Incorrect permissions can prevent Apache or Nginx from reading vital application files, simulating a login lockout. Enforce the security standard:

# Set permissions for directories (755) and files (644)
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;

# Hardening for the sensitive configuration file
chmod 400 wp-config.php

# Grant ownership to the user running the web server
chown -R www-data:www-data /var/www/html

4.2 inspecting .htaccess integrity#

If you run Apache, orphaned or duplicate rewrite rules can block admin access.

# Verify the complete content
cat .htaccess

# Search for security block markings
grep -n "BEGIN\|END" .htaccess

# Check the count of BEGIN blocks
grep -c "BEGIN" .htaccess

If duplicate security blocks or repeated rules are present, clean the file and regenerate the basic WordPress permalinks.


5. Access Recovery: Admin Provisioning and Secure Password Resets via WP-CLI and MySQL#

If the WordPress core is installed and functional (wp core is-installed), we can use the CLI to manage plugins.

5.1 deactivating specific plugins#

Identify the active security plugin and deactivate it using its technical slug:

# List active plugins
wp plugin list --status=active

# Deactivate the problematic plugin
wp plugin deactivate all-in-one-wp-security-and-firewall

If the plugin crashes the WP-CLI bootstrap itself, bypass plugin loading with the --skip-plugins flag:

wp plugin deactivate all-in-one-wp-security-and-firewall --skip-plugins

5.2 global deactivation (emergency)#

If you do not know which plugin caused the lockout, deactivate all active plugins at once:

# Deactivate all active plugins in one command
wp plugin deactivate $(wp plugin list --status=active --field=name) --skip-plugins

# Or using the direct all flag
wp plugin deactivate --all --skip-plugins

Slugs of other common security plugins:#


If you are locked out of WordPress, this guide covers both production-safe recovery paths:

  1. create a new administrator account;
  2. reset password for an existing account.

WordPress uses different user roles, each with specific permissions:

Warning: Permissions may vary depending on your installation and plugins.

Emergency Admin User Creation via WP-CLI#

Via wp-cli#

If WP-CLI is available, creating an administrator is straightforward.

Installation guide:

<https://domain.com/article/instalando-wpcli-na-hospedagem/>

Create a new administrator:

php wp user create USERNAME EMAIL --role=administrator --user_pass="PASSWORD"

Verify creation:

php wp user list

Direct Database Provisioning (Fallback without WP-CLI)#

If WP-CLI is not available, create the admin user directly in the database.

Identify the database used by WordPress:

grep DB wp-config.php

Connect to MySQL/MariaDB:

mysql -u USER -p

If login succeeds, you should see:

MariaDB [(none)]:

Select the database:

use DATABASE_NAME;

List tables to identify the prefix:

show tables;

Sample output:

| wp_e_events |
| wp_e_submissions |
| wp_expm_maker_pages |
| wp_ezoic_endpoints |

In this case, prefix is wp_.

Create the new admin user:

INSERT INTO wp_users (user_login, user_pass, user_nicename, user_email, user_status, display_name)
VALUES ('USERNAME', MD5('USER_PASSWORD'), 'FULL NAME', 'USER_EMAIL', 0, 'FULL NAME');

Practical example:

INSERT INTO wp_users (user_login, user_pass, user_nicename, user_email, user_status, display_name)
VALUES ('usuario', MD5('mudar123'), 'Usuario Teste', '[email protected]', 0, 'Usuario Teste');
Query OK, 1 row affected (0.001 sec)

Check assigned ID:

SELECT ID FROM wp_users WHERE user_login = 'USERNAME';

Expected output:

SELECT ID FROM wp_users WHERE user_login = 'usuario';
+----+
| id |
+----+
| 90 |
+----+
1 row in set (0.001 sec)
Warning: ID changes for every new user. In the example above, ID is 90.

Grant administrator capabilities:

INSERT INTO wp_usermeta (user_id, meta_key, meta_value)
VALUES (USER_ID, 'wp_capabilities', 'a:1:{s:13:"administrator";b:1;}');

Example:

INSERT INTO wp_usermeta (user_id, meta_key, meta_value)
VALUES (90, 'wp_capabilities', 'a:1:{s:13:"administrator";b:1;}');
Query OK, 1 row affected (0.004 sec)

Also add user level:

INSERT INTO wp_usermeta (user_id, meta_key, meta_value)
VALUES (USER_ID, 'wp_user_level', '10');

Example:

INSERT INTO wp_usermeta (user_id, meta_key, meta_value)
VALUES (90, 'wp_user_level', '10');
Query OK, 1 row affected (0.001 sec)

Done. The user now has administrator permissions and can log in to wp-admin.

Secure Password Reset Procedures (WP-CLI and MySQL)#

If the account already exists, password reset is usually faster than creating a new admin.

With wp-cli#

php wp user list
php wp user update USER_ID --user_pass="NEW_STRONG_PASSWORD"

Emergency database reset#

UPDATE wp_users
SET user_pass = MD5('newPassword123')
WHERE user_login = 'username';
Important: after regaining access, change the password again in WordPress admin so the platform stores a modern password hash.

6. Manual web server contingencies#

If the database or WP-CLI is completely inaccessible, use these filesystem fallbacks directly in the terminal:

6.1 move the physical plugin directory#

Renaming the folder forces WordPress to skip the plugin during bootstrap:

mv wp-content/plugins/all-in-one-wp-security-and-firewall \
   wp-content/plugins/all-in-one-wp-security-and-firewall.bak

6.2 temporarily disable ModSecurity (Apache WAF)#

If ModSecurity is active and triggering false positives on /wp-admin/, you can check its status and temporarily disable it:

# Verify if the module is loaded in Apache
apache2ctl -M 2>/dev/null | grep security

# ModSecurity audit logs
tail -n 50 /var/log/apache2/modsec_audit.log

If necessary, disable it via .htaccess for a quick test:

<IfModule mod_security.c>
    SecRuleEngine Off
</IfModule>

6.3 validate Nginx settings and logs#

If the stack runs Nginx, check static blocking rules in the configuration:

# Test Nginx configurations syntax integrity
nginx -t

# Search for deny directives or 403 returns
grep -rn "deny\|return 403" /etc/nginx/

# Consult access logs for HTTP 403 errors
tail -n 50 /var/log/nginx/access.log | grep 403

7. Proxy/cdn headers: incorrect source IP#

In server configurations behind proxies or CDNs like Cloudflare, security plugins might read the reverse proxy IP instead of the client IP. If the proxy fails to authenticate or triggers a threshold, the plugin will block the proxy's IP, effectively locking out all site administrators.

Ensure headers such as X-Forwarded-For or CF-Connecting-IP are correctly configured to pass the client's real IP into the WAF settings.


8. Post-fix verification and rollback#

8.1 validating restored access#

Ensure that the REST API and critical admin endpoints are responding properly:

# Test HTTP access to the admin area (expected redirect or form)
curl -I https://domain.com/wp-admin/

# Test direct HTTP access to the login form
curl -I https://domain.com/wp-login.php

# Test REST API integrity
curl -I https://domain.com/wp-json/

8.2 safe rollback runbook#

If deactivating the plugin breaks application functionality or if you need to revert the configurations:

# 1. Reactivate the deactivated plugin
wp plugin activate all-in-one-wp-security-and-firewall

# 2. Restore the renamed directory
mv wp-content/plugins/all-in-one-wp-security-and-firewall.bak \
   wp-content/plugins/all-in-one-wp-security-and-firewall

# 3. Restore the original .htaccess configuration
cp .htaccess.bak.* .htaccess

9. Complete diagnostic script and checklist#

9.1 forensic diagnostic script (wp-diagnostic.sh)#

Create the following script in your WordPress root directory to audit the health of your installation:

#!/bin/bash
# wp-diagnostic.sh - Forensic WordPress diagnostic
# Run as owner of files or root

set -euo pipefail

DOMAIN="${1:-localhost}"
WP_PATH="${2:-.}"

echo "=== WordPress Diagnostic: $DOMAIN ==="
echo ""

# 1. HTTP Test
echo "[1] Testing HTTP connections..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -I "https://$DOMAIN/wp-admin/")
echo "    HTTP Status for /wp-admin/: $HTTP_CODE"

if [ "$HTTP_CODE" = "403" ]; then
    echo "    ⚠️ Warning: Block detected at the server/WAF layer (HTTP 403)"
elif [ "$HTTP_CODE" = "200" ]; then
    echo "    ✅ Status HTTP 200. If screen is blank, check PHP logs."
fi

# 2. Check Core
echo ""
echo "[2] Validating WordPress core..."
cd "$WP_PATH"
if wp core is-installed 2>/dev/null; then
    echo "    ✅ Core is installed"
    wp core verify-checksums 2>/dev/null || echo "    ⚠️ Warning: Core checksum verification failed!"
else
    echo "    ❌ Core not installed or database inaccessible."
fi

# 3. Active Plugins
echo ""
echo "[3] Top active plugins:"
wp plugin list --status=active --field=name 2>/dev/null | head -n 10

# 4. Check .htaccess
echo ""
echo "[4] Auditing .htaccess..."
if [ -f .htaccess ]; then
    BLOCKS=$(grep -c "BEGIN" .htaccess)
    echo "    Identified blocks: $BLOCKS"
    [ "$BLOCKS" -gt 1 ] && echo "    ⚠️ Warning: Duplicate blocks detected in .htaccess"
else
    echo "    ℹ️ .htaccess file not found."
fi

# 5. Recent Logs
echo ""
echo "[5] Inspecting debug.log..."
if [ -f wp-content/debug.log ]; then
    tail -n 5 wp-content/debug.log
else
    echo "    ℹ️ debug.log not found."
fi

echo ""
echo "=== End of Diagnostic ==="

9.2 checklist: WordPress admin locked#

Use this checklist to guide your mitigation process step by step:


10. Production Takeaways and Operational Governance#

Blocking /wp-admin before login is not solved with random attempts. It's solved with a methodical analysis of network and application layers, forensic log auditing, and surgical WP-CLI execution.

By following a well-documented runbook, Mean Time to Recovery (MTTR) drops drastically, and critical credentials remain secure.

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments