TechEarl

WPScan Usage Guide and Man Page (2026)

WPScan v3.8+ usage reference for WordPress security audits: install on Linux/macOS, API token setup, the command patterns that matter (enumerate users, vulnerable plugins, brute force), JSON output, and how WPScan compares to Wordfence, Sucuri, and WPSec.

Ishan KarunaratneIshan Karunaratne⏱️ 16 min readUpdated
WPScan v3.8+ usage reference for WordPress security audits: install on Linux/macOS, API token setup, the command patterns that matter (enumerate users, vulnerable plugins, brute force), JSON output, and how WPScan compares to Wordfence, Sucuri, and WPSec.

WPScan is the black-box WordPress scanner that has been the de facto standard for WordPress pentesting since around 2011. It enumerates users, fingerprints WordPress core/plugin/theme versions, checks them against a curated vulnerability database, and (with the right wordlist) brute-forces login forms. The old "ruby ./wpscan.rb" invocation from the 2015-era docs is long gone, current WPScan is a polished Ruby gem with sane defaults, a vulnerability API that requires a free token, and a Docker image that saves you from gem-install pain. This is the current usage reference for WPScan v3.8+: install paths, API token setup, the dozen command patterns that cover 95% of real audits, JSON output for CI integration, the legal caveat that matters, and how it compares to Wordfence, Sucuri, and WPSec.

How do I use WPScan to audit WordPress security?

Install WPScan as a Ruby gem (sudo gem install wpscan), as a Docker container (docker run --rm -it wpscanteam/wpscan), or use the preinstalled binary on Kali Linux. Get a free API token from wpscan.com to enable the vulnerability database (free tier: 25 requests/day, paid tiers from $99/month for unlimited). The most useful invocation is wpscan --url https://example.com --api-token YOUR_TOKEN --enumerate vp,vt,u --random-user-agent, that scans WordPress core, enumerates vulnerable plugins and themes, lists users, and rotates user agents to avoid trivial blocking. Add --output report.json --format json for machine-readable output, --disable-tls-checks only when the target uses self-signed certs, and --throttle 500 to slow requests when targets rate-limit. Never scan a site you do not own or have written permission to test, unauthorized scanning is a crime in most jurisdictions.

Try it with your own values

Set your target URL, the username and wordlist for brute-force tests, and the output file. Every WPScan command below updates so you can copy and run against an authorized target.

Jump to:

Install WPScan

Three reliable paths. Pick whichever fits your workflow.

As a Ruby gem (Linux/macOS):

bash
# Prereqs: Ruby 3.0+, libcurl, build tools
sudo apt install ruby ruby-dev libcurl4-openssl-dev build-essential -y  # Debian/Ubuntu
brew install ruby                                                       # macOS

# Install the gem
sudo gem install wpscan

# Confirm
wpscan --version

The gem installs wpscan into your PATH. First run also pulls down a fresh local copy of the vulnerability database fingerprints (separate from API lookups, fingerprints identify versions, the API identifies known vulns).

As a Docker container:

bash
docker pull wpscanteam/wpscan
docker run --rm -it wpscanteam/wpscan --url :target

The container path avoids Ruby version conflicts and is what I reach for on machines I do not want to install gems on. The image is maintained by the WPScan team at hub.docker.com/r/wpscanteam/wpscan.

On Kali Linux:

wpscan is preinstalled on Kali. apt update && apt install wpscan to bump it to the latest if needed.

The current version in 2026 is v3.8.x. Older wpscan v2 (the "ruby ./wpscan.rb" era from 2015) is dead, if you find documentation telling you to clone a repo and run ruby ./wpscan.rb, that doc is a decade out of date.

The WPScan API token

WPScan's vulnerability data lives in a curated database that is gated behind an API token. The token is free for personal use with a daily quota; paid plans lift the cap.

Sign up at wpscan.com/api. Tiers as of 2026:

TierDaily requestsCost
Free25/day$0
Standard75/day$99/month
Plus200/day$199/month
EnterpriseUnlimited / customContact sales

Each request to the vulnerability database counts as one, a single wpscan run on a site with 30 plugins consumes around 31 requests (one per plugin + core). The 25/day free tier is enough for occasional audits of small sites; serious pentesting or CI integration needs a paid tier.

Pass the token on every invocation:

bash
wpscan --url :target --api-token YOUR_TOKEN_HERE

Or put it in a config file at ~/.wpscan/scan.yml:

yaml
cli_options:
  api_token: YOUR_TOKEN_HERE

wpscan reads ~/.wpscan/scan.yml automatically. The config-file approach keeps the token out of your shell history. Permission the file with chmod 600 ~/.wpscan/scan.yml.

If you skip the token, WPScan still runs but reports No WPVulnDB API Token given, as a result vulnerability data has not been output. The version fingerprinting still works, you just have to look up CVEs manually.

The command patterns that matter

A working WPScan vocabulary. Each of these is an invocation I have used in a real audit.

Basic scan (the default starting point):

bash
wpscan --url :target --api-token YOUR_TOKEN

Detects WordPress version, theme, a sample of plugins, the readme, and the wp-content directory. Cheap, fast, no enumeration. Use this first to confirm the target is WordPress and the scanner can reach it.

Enumerate users:

bash
wpscan --url :target --api-token YOUR_TOKEN --enumerate u

Discovers usernames by walking author IDs (/?author=1, /?author=2, …). Most WordPress installs leak usernames this way even with author pages disabled. Add u1-100 to widen the search range: --enumerate u1-100.

Enumerate vulnerable plugins:

bash
wpscan --url :target --api-token YOUR_TOKEN --enumerate vp

vp = "vulnerable plugins only". ap enumerates all plugins (much slower and noisier). p is the default and falls between. For an audit, vp is what you usually want, known-vulnerable plugins are the actionable result.

Enumerate vulnerable themes:

bash
wpscan --url :target --api-token YOUR_TOKEN --enumerate vt

Same logic as plugins. vt = vulnerable, at = all, t = a "passive" default mix.

Combine enumeration:

bash
wpscan --url :target --api-token YOUR_TOKEN \
  --enumerate vp,vt,u,dbe,cb,tt

Comma-separated values run in one pass. Useful sub-enumerates:

  • vp, vulnerable plugins
  • vt, vulnerable themes
  • u, users
  • dbe, database export files (look for accidentally exposed dumps)
  • cb, config backups (wp-config.php.bak, wp-config.old, …)
  • tt, timthumb scripts (very old vuln but still kicking around)

Brute force a specific user:

bash
wpscan --url :target --api-token YOUR_TOKEN \
  --usernames :username --passwords :wordlist \
  --max-threads 5

--max-threads 5 is sensible, too many parallel attempts trip Cloudflare and most WAFs. --passwords takes a wordlist file; rockyou.txt is the classic but contains 14M entries and will take days at honest rate limits. Trim to top 10K passwords for realistic timeframes.

Brute force multiple users from a file:

bash
wpscan --url :target --api-token YOUR_TOKEN \
  --usernames users.txt --passwords passwords.txt

users.txt should be the output of an earlier --enumerate u run, one username per line.

Random user agent + request delay (stealthier scan):

bash
wpscan --url :target --api-token YOUR_TOKEN \
  --random-user-agent --throttle 1000 --request-timeout 60

--throttle 1000 waits 1000ms between requests. --random-user-agent rotates the UA string per request. Neither bypasses a real WAF but both help against naive blocking.

Skip TLS verification (testing with self-signed certs):

bash
wpscan --url :target --api-token YOUR_TOKEN --disable-tls-checks

Only use this on hosts you control. Disabling TLS verification on the public internet defeats half the point of HTTPS.

Behind a proxy (for SOCKS5 / TOR routing):

bash
wpscan --url :target --api-token YOUR_TOKEN \
  --proxy socks5://127.0.0.1:9050

The classic TOR-via-WPScan invocation. Combine with --random-user-agent and --throttle for slower but quieter scans. Note: TOR exit IPs are aggressively blocked by Cloudflare and most managed-WordPress hosts.

Update the local fingerprint database:

bash
wpscan --update

Pulls the latest core/plugin/theme fingerprints. Run weekly if you scan often.

Output formats: JSON and CLI

Default output is CLI-formatted with colored output. For automation, use --format json:

bash
wpscan --url :target --api-token YOUR_TOKEN \
  --output :output_file --format json

The JSON includes every finding, version, vulnerability ID, and timing. Pipe it into jq for filtering:

bash
# Count vulnerable plugins
jq '.plugins | to_entries | map(select(.value.vulnerabilities | length > 0)) | length' :output_file

# List CVEs for everything
jq '.plugins | to_entries[] | .value.vulnerabilities[]?.references.cve[]?' :output_file

For CI integration (e.g. GitHub Actions running a weekly scan against staging), JSON output + jq is the right shape. Fail the build if vulnerabilities of a certain severity show up:

bash
COUNT=$(jq '.plugins | to_entries | map(.value.vulnerabilities[]?) | length' :output_file)
if [ "$COUNT" -gt 0 ]; then
  echo "Found $COUNT plugin vulnerabilities"
  exit 1
fi

Other formats: --format cli-no-color for plain text, --format cli-no-colour for the British spelling.

Read this twice. Running WPScan against a site you do not own or have explicit written permission to test is a crime in most jurisdictions:

  • United States, Computer Fraud and Abuse Act (CFAA). Scanning is in a gray area; brute force is unambiguously illegal.
  • United Kingdom, Computer Misuse Act 1990. Unauthorized access (and attempted access) is a criminal offense.
  • European Union, Directive 2013/40/EU on attacks against information systems. National implementations vary; assume scanning without authorization is illegal.

What counts as authorization:

  • A signed pentesting contract or statement of work.
  • A site you personally own and operate.
  • A bug bounty program that explicitly scopes WPScan-style scanning (many do not, read the program rules first).

What does not count:

  • "It is publicly accessible, so it is fair game." (No.)
  • "I am only enumerating users, not exploiting anything." (Enumeration is unauthorized access in many jurisdictions.)
  • "I scanned my client's competitor to compare." (Definitely not.)

When in doubt, do not scan. The cost of a misjudged scan can be a criminal charge or a civil suit. If you find a vulnerability on a site you do not own and want to report it, contact the site directly, most have a security.txt at /.well-known/security.txt with a disclosure address.

WPScan vs Wordfence vs Sucuri vs WPSec

Four tools, different roles. Pick by what you are trying to do.

ToolTypeWhere it runsBest forPricing
WPScanBlack-box CLI scannerYour machine (external view)Pentests, audits, CI checksFree CLI; API tiers from $0-199+/mo
WordfenceWordPress pluginInside WordPress (white-box)Production monitoring, WAF, malware scanFree plugin; Premium $119/year/site
SucuriSaaS WAF + scannerCloud (in-line WAF) + scannerDDoS protection, malware cleanup$200-$500+/year
WPSecSaaS scannerCloud (external view)One-off audits without local installFree with limits; paid tiers

The honest take:

  • WPScan is the right tool for an audit or pentest where you want the attacker's view from outside. It does not require touching the target's WordPress install.
  • Wordfence is the right tool for ongoing production protection, runs inside WordPress, has access to all the files and DB, can scan for malware and block traffic in real time.
  • Sucuri is the right call if you need a cloud WAF that sits in front of WordPress and also handles DDoS, overlapping with Cloudflare but with WordPress-specific rules.
  • WPSec fills the same niche as WPScan but as a hosted service. Easier to integrate into reports without explaining "I ran a CLI tool."

For most security workflows, you end up with WPScan for periodic external audits and Wordfence (or a competitor) running inside the WordPress installs you control. They are complementary, not competitors.

The man page

The current wpscan --help output is comprehensive but long. The most useful flags, grouped:

Target selection:

code
--url | -u <target_url>          The WordPress URL/domain to scan.
--force | -f                     Do not check whether the target is WordPress.
--server <server>                Force the server type (apache, nginx, iis).

Authentication and rate control:

code
--api-token | -t <token>         The WPScan API token for vulnerability data.
--max-threads <number>           Maximum threads to use (default: 5).
--throttle <ms>                  Delay between requests in milliseconds.
--request-timeout <s>            Request timeout in seconds.
--connect-timeout <s>            Connection timeout in seconds.

Enumeration:

code
--enumerate | -e [opts]          Enumeration options. Multiple values allowed.
  vp                             Vulnerable plugins only
  ap                             All plugins (slower)
  p                              Popular plugins (default mix)
  vt                             Vulnerable themes only
  at                             All themes
  t                              Popular themes
  tt                             Timthumb files
  cb                             Config backups
  dbe                            Database exports
  u[1-N]                         Users from id 1 to N (default 1-10)
  m[1-N]                         Media from id 1 to N

Brute force:

code
--usernames <list>               Comma-separated usernames or path to file.
--passwords | -P <path>          Path to a password wordlist.
--password-attack <type>         wp-login, xmlrpc, or xmlrpc-multicall.

Output:

code
--output | -o <path>             Write output to a file.
--format <fmt>                   cli, cli-no-color, json.
--detection-mode <mode>          mixed, passive, or aggressive.
--no-banner                      Skip the ASCII banner.
--verbose | -v                   Verbose output.

Stealth and proxying:

code
--random-user-agent              Use a random UA per request.
--user-agent | -a <ua>           Set a specific UA string.
--proxy <[scheme://]host:port>   HTTP/HTTPS/SOCKS proxy.
--proxy-auth <user:pass>         Proxy credentials.
--http-auth <user:pass>          HTTP Basic auth for the target.
--cookie-string <s>              Cookie string for the target.
--cookie-jar <path>              Cookie jar file.
--headers <headers>              Custom request headers.
--vhost <vhost>                  Set the Host: header (for hosts hidden behind shared IPs).

Other:

code
--disable-tls-checks             Skip certificate verification.
--update                         Update the local fingerprint database.
--no-update                      Do not check for updates.
--scope <scope>                  Limit scope to specific subdomains/paths.
--exclude-content-based <regex>  Exclude content matching this pattern.
--ignore-main-redirect           Ignore redirects from the main URL.
--help | -h                      Show help.
--hh                             Show all help options (advanced).
--version                        Show WPScan version.

For the latest, run wpscan --hh (double-h) which shows every option including the rarely-used ones.

Troubleshooting

"The remote website is up, but does not seem to be running WordPress." Either the target is not WordPress, or WordPress is heavily customized (renamed wp-content directory, hidden version strings, blocked common paths). Try --force to bypass the check and --wp-content-dir, --wp-plugins-dir to point at the actual paths.

Cloudflare returns 403 / "Just a moment..." on every request. Cloudflare's bot detection is aggressive. Options: contact the site owner to whitelist your scanning IP, route via a residential proxy, or use the --random-user-agent + --throttle combo to look less like a bot. For sites behind aggressive bot protection, an authenticated scan (with --cookie-string from a valid session) often works better.

API requests fail with "401 Unauthorized". Token is invalid or expired. Regenerate at wpscan.com/profile. Confirm the token is being passed correctly, wpscan --url ... --api-token TOKEN_HERE with the literal token, not the word YOUR_TOKEN.

API quota exhausted ("Your daily request limit has been reached"). Free tier is 25/day. Either wait until the daily reset (00:00 UTC), upgrade your plan, or scope the scan tighter, --enumerate vp instead of --enumerate ap cuts requests significantly.

Brute force never finds the password even though I know it. Either the target has rate-limited or banned your IP, the username is wrong, or you are hitting the wrong endpoint. Try --password-attack xmlrpc instead of the default wp-login, xmlrpc.php often has weaker rate limiting. If xmlrpc.php returns 405 Method Not Allowed, it has been disabled (a legitimate hardening step).

WPScan reports a plugin version but no vulnerabilities, even though I see CVEs. The vulnerability database lags new CVEs by a few days to a few weeks. Cross-check with WPScan's public vuln search or the WordPress Plugin Vulnerabilities page. Also possible: the vulnerability exists in a different plugin version than the one detected.

False positive on user enumeration. Some WordPress sites return a generic 200 for every /?author=N URL (a hardening measure). WPScan may report the user IDs as "found" even though they do not exist. Cross-check with /wp-json/wp/v2/users (which is the REST API endpoint that also leaks usernames if not locked down).

What to do next

FAQ

TagsWordPressSecurityWPScanPenetration TestingVulnerability ScanningPentesting
Share
Ishan Karunaratne

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts