# WordPress security from the command line: WP-CLI commands

Run a WordPress security audit, preview hardening fixes, read the activity log and release lockouts from the terminal with BetterShield’s WP-CLI commands.

By M Asif Rahman, Oct 21, 2026. Updated Nov 3, 2026.

To manage WordPress security from the command line, use the fifteen `wp bettershield` commands that BetterShield adds to WP-CLI. They run an audit, list findings, preview and undo hardening fixes, read the activity log, release lockouts and recover access from a terminal. They use the same code as the dashboard, so the terminal and the screens always agree. This guide shows what each command is for, the pattern they all follow, and the settings we recommend.

It builds on the [hardening guide](/blog/wordpress-hardening/), whose fixes the `harden` command applies, and on the [WordPress security checklist](/blog/wordpress-security-checklist/), whose checks the `audit` command runs.

## Quick summary

- BetterShield adds **fifteen** `wp bettershield` commands: `audit`, `findings`, `harden`, `activity`, `agents`, `verify_log`, `recover`, `lockdown`, `lockouts`, `blocklist`, `integrity`, `alerts`, `settings`, `vulnerabilities` and `diagnostics`.
- Reading needs no user. A command that **changes** the site needs `--user=<login>`, naming an account that can manage BetterShield, and the change is recorded against it.
- Changes made this way are logged as coming through WP-CLI, in the same [activity log](/blog/wordpress-activity-log/) as changes made in the dashboard.
- `wp bettershield recover` never asks for a user, because it exists for the day nobody can sign in.
- `audit` can exit non-zero when the score or a finding crosses a line you set, which makes it usable as a gate in a script.

## Why use the terminal at all

The dashboard is the right place to learn what BetterShield does. The terminal is the right place once you manage more than one site, want a record in a script, or cannot reach the dashboard. [WP-CLI](https://make.wordpress.org/cli/handbook/) is WordPress’s own command-line interface, and commands from plugins appear beside the core ones. Nothing here needs a special setup beyond WP-CLI itself.

Because the commands share code with the dashboard, there is no second set of rules to learn. A fix applied by `wp bettershield harden` runs the same code as one applied in the dashboard, and the activity log notes that it came through WP-CLI.

## The pattern every command follows

Two rules cover almost everything.

1. **Reading is free.** `audit`, `findings`, `activity`, `agents`, `verify_log` and the list forms of `lockouts` and `blocklist` change nothing and need no user.
2. **Changing needs a named person.** Add `--user=<login>` with an account that can manage BetterShield. The change is recorded against that account, so the log says who did it even though no browser was involved.

```
wp bettershield harden xmlrpc --undo --user=admin
```

Most list commands also take `--format=`, with `table`, `json` or `csv` depending on the command, so the output can go straight into a spreadsheet or another tool. Times print in the site’s time zone, and an option a command does not use is refused rather than ignored.

## The commands, grouped by job

| Job | Commands |
|---|---|
| See where the site stands | `audit`, `findings` |
| Change a setting | `harden`, `settings` |
| Read what happened | `activity`, `agents`, `verify_log` |
| Get back in or hold the line | `recover`, `lockdown` |
| Manage who is paused or refused | `lockouts`, `blocklist` |
| Check files and advisories | `integrity`, `vulnerabilities` |
| Manage who gets alert email | `alerts` |
| Ask for help | `diagnostics` |

### Audit and findings

`wp bettershield audit` runs the read-only audit and prints the score, the grade and the open findings by severity. Two options turn it into a gate:

- `--fail-under=<score>` exits non-zero when the score is below a whole number from 0 to 100.
- `--fail-on=<severity>` exits non-zero when an open finding is at that severity or worse: `critical`, `high`, `medium` or `low`.

```
wp bettershield audit --fail-under=80 --fail-on=high
```

The score changed in 1.1.3: it is now 60 points for setup essentials and 40 for site health, and it is capped at 74 while a critical finding is open. Your score will move after you update, so recheck any `--fail-under` threshold in a deploy script before you rely on it.

`--format=` takes `summary` (the default) or `json`. `wp bettershield findings` lists findings from the last audit with the ID of the fix where there is one. `--status=` takes `open` (the default), `snoozed`, `suppressed`, `fixed` or `all`.

```
wp bettershield findings --status=snoozed
```

What the audit looks at, and what it misses, is covered in the [security audit guide](/blog/wordpress-security-audit/).

### Hardening

`wp bettershield harden` lists the hardening fixes, or previews, applies or undoes one. Leave out the fix’s ID and it lists every fix with its ID and state. Three options matter most:

- `--dry-run` describes what applying would do and changes nothing. It cannot be combined with `--undo`.
- `--undo` reverts the fix.
- `--force` applies a sign-in fix even though the recovery check failed. It is recorded in the activity log.

```
wp bettershield harden login_url --slug=side-door --user=admin
wp bettershield harden xmlrpc --undo --user=admin
```

The recovery check is there for a reason: BetterShield makes sure a recovery link or printed code works before a change to how people sign in. We recommend leaving `--force` alone unless you have another way back in.

### Activity and agents

`wp bettershield activity` shows the activity log, newest first. It takes `--limit=<number>` (at most 500, default 40), `--area=` (`access`, `exposure`, `server`, `configuration`, `extensions` or `plugin`), `--search=`, `--actor=`, a `--from=` and `--to=` day range, and `--format=`. On a multisite network `--sites=` takes site IDs or `all`; for the network screens that sit beside it, see [WordPress multisite security](/blog/wordpress-multisite-security/). In CSV format it exports every matching row:

```
wp bettershield activity --from=2026-08-01 --to=2026-08-15 --format=csv > august.csv
```

`wp bettershield agents` shows every ability call on the site from any plugin, and needs WordPress 7.1. `--outcome=permission_denied` is a quick way to see what an assistant was refused. `wp bettershield verify_log` checks the activity log against its daily seals and exits non-zero if a sealed day was altered, removed or could not be read.

### Recovery and lockdown

`wp bettershield recover` turns on safe mode: BetterShield’s protections pause and the standard sign-in page answers again. No setting is lost, and rules BetterShield wrote to .htaccess and index files stay in place. `--hours=` sets how long, from 1 to 24 (the default is 1), `--end` ends it early, and `--new-link` also issues a fresh recovery link, printed once, which makes the previous one stop working.

```
wp bettershield recover --hours=4 --new-link
```

`wp bettershield lockdown` shows, turns on or lifts the site lockdown. `status` needs no user, while `on` and `off` need `--user=<login>`. From a terminal there is no browser session to keep, so `on` signs out every dashboard session, including your own. The recovery link and safe mode still outrank it. If you are already locked out, start with [getting back in](/blog/locked-out-of-wordpress/).

### Lockouts and the blocklist

`wp bettershield lockouts` lists lockouts on the sign-in form and the public forms, or releases them. Releasing needs `--user=<login>` and is recorded in the activity log. `--surface=` limits it to `signin`, `comment`, `reset` or `register`, and `--active` shows only the lockouts running now.

```
wp bettershield lockouts list --active
wp bettershield lockouts release --all --surface=signin --user=admin
```

`wp bettershield blocklist` lists **Never allowed to sign in**, or adds or removes an entry with the same checks as **Protection › Login & Access**. An entry can name a network with `--range=`, part of a user agent with `--ua=` (at least four characters), a username with `--username=`, or a combination, and all of them have to match.

```
wp bettershield blocklist add --range=203.0.113.0/24 --note="Scanner" --user=admin
```

The thinking behind both lists is in the [allowlist and blocklist guide](/blog/wordpress-ip-allowlist-blocklist/).

### Files, settings and the rest

`wp bettershield integrity` compares core, directory-plugin and supported WPDeveloper paid-plugin files against the published copies. `status` and `scan` need no user; `restore` puts the published copy back, sends the current file to quarantine and prints an undo token. `--fail-on-changes` makes it exit non-zero when any file differs. The list of open changes comes a page at a time: `--change=` (`modified`, `added`, `removed`, `unreadable` or `package_mismatch`) and `--location=` (`core`, `plugin`, `theme`, `muplugin`, `dropin`, `config`, `unlisted` or `extra`) narrow it, a cut table prints the command for the next page with `--cursor=`, and `--format=csv` exports every match.

```
wp bettershield integrity --change=added --location=core
wp bettershield integrity --format=csv > changes.csv
```

See [the file integrity check](/blog/wordpress-file-integrity-check/) for what the screen shows.

`wp bettershield settings export posture.json` writes BetterShield’s settings as JSON. `import` only shows its plan until you add `--apply`, which is what makes it safe to use for rolling one site’s setup out to the next:

```
wp bettershield settings import posture.json --apply --user=admin
```

`wp bettershield vulnerabilities` shows the optional feed’s state; `check` requests a refresh after consent. A missing or failed result is never reported as clean. See [Vulnerability checks](/docs/vulnerability-checks/).

## Manage alerts and host deployments

Free 1.1.5 adds `wp bettershield alerts` for the people who get security email. Listing recipients needs no user; every change needs `--user=<login>`. `recipients list` takes `--format=` with `table`, `json` or `csv` and shows what each address chose to receive.

```sh
wp bettershield alerts recipients list --format=json
wp bettershield alerts recipients add security@example.com --user=admin
wp bettershield alerts recipients remove security@example.com --user=admin
wp bettershield alerts recipients reset --user=admin
wp bettershield alerts mute --user=admin
wp bettershield alerts unmute --user=admin
```

`recipients reset` goes back to the site administration email as the fallback. Adding a recipient or unmuting does not turn on the instant-alert or weekly-summary switch; on a new install both stay off until you turn them on in Quick Setup or Settings. The longer story is in the [email alerts guide](/blog/wordpress-security-email-alerts/) and [Alert recipients](/docs/alert-recipients/).

A host that sends its own routine notices can take them over:

```sh
wp bettershield alerts host-managed on --user=admin
wp bettershield alerts host-managed off --user=admin
```

Host-managed mode holds routine events for the weekly summary. While instant alerts are enabled, a new administrator, a role raised to administrator, the trap password and a changed code file still email at once. It does not enable either email switch. See [Host-managed alerts](/docs/host-managed-alerts/).

Two `integrity` actions are for deployments that happen outside WordPress’s updater. Neither one checks who wrote the code or whether it is safe, so confirm the change against your deployment record first.

```sh
wp bettershield integrity record-update --slug=your-plugin --version=1.2.3 --user=admin
wp bettershield integrity expect-file --file=.htaccess --user=admin
```

`record-update` records a known plugin or theme update (add `--type=theme` for a theme); the component needs an earlier recorded copy and a version that actually changed, and changes that are already open stay open. `expect-file` accepts one reviewed host-written file as its expected state and prints an undo token. For a paid plugin, `integrity expect` previews a group of changes for one version, and `--yes` accepts it for that version only:

```sh
wp bettershield integrity expect --slug=your-paid-plugin --version=1.2.3
wp bettershield integrity expect --slug=your-paid-plugin --version=1.2.3 --yes --user=admin
```

`wp bettershield integrity undo --token=<token> --user=admin` brings an accepted change back for review. See [Record host file changes](/docs/record-host-file-changes/) and [Expected file changes](/docs/expected-file-changes/).

## What it can break, and how to undo

- **A hardening fix you did not mean to apply.** Preview with `--dry-run` first. Undo with the same command plus `--undo`.
- **A sign-in change that locks people out.** BetterShield runs its recovery check before a sign-in change. `--force` skips that protection, so use it deliberately. `wp bettershield recover` is the way back.
- **A lockdown that ends your own session.** `lockdown on` from a terminal signs out every dashboard session, yours included. Run `lockdown off --user=<login>` to lift it.
- **A file change accepted too soon.** `record-update`, `expect-file` and `expect --yes` can hide that exact change. Keep the printed undo token; a later edit to the file is still reported.
- **A settings import that changes more than you expected.** Run it without `--apply` first and read the plan. Blocklist changes can also be reversed under **Settings history** on **Settings › General**.

## Check it is working

- `wp bettershield audit` prints a score and grade.
- After a change, `wp bettershield activity --limit=5` shows the entry, recorded as coming through WP-CLI and attributed to the account you passed.
- `wp bettershield harden` with no ID lists each fix and its state.
- `wp bettershield verify_log` exits cleanly when the activity log matches its seals.

## Recommended settings

We recommend using the terminal for reading by default and for changes you can preview.

- **Gate a deployment:** run `wp bettershield audit --fail-under=80 --fail-on=high` after a deploy, with a threshold that suits the site.
- **Always dry-run first:** `harden --dry-run` and an `import` without `--apply` both show a plan and change nothing.
- **Name a real person for `--user`:** the log is only as useful as the account it points to.
- **Keep a settings export:** `settings export` before a batch of changes is a cheap way back.
- **Looking after several sites:** a script covers each one, and the free hub lists every site’s score in one place. [Manage security across many WordPress sites from one hub](/blog/manage-security-multiple-wordpress-sites/) walks through it.

BetterShield Ultra adds `wp bettershield-ultra policy`, `grants`, `channels`, `edge` and `report`, which only read and so need no `--user`. The free commands above need nothing else.

## Frequently asked questions

### Do I need a user for every WP-CLI command?

No. Reading commands such as `audit`, `findings`, `activity` and `agents` need no user. A command that changes the site needs `--user=<login>`, and `recover` never asks for one.

### Can I run a WordPress security audit from the command line?

Yes. `wp bettershield audit` runs the read-only audit and prints the score, grade and open findings. Add `--format=json` for machine-readable output.

### How do I make a script fail when the score is too low?

Use `--fail-under=<score>` with a whole number from 0 to 100, or `--fail-on=<severity>` to fail on an open finding at that severity or worse. Both make the command exit non-zero.

### Are changes made in WP-CLI recorded?

Yes. They go into the activity log and are logged as coming through WP-CLI, attributed to the account you named with `--user`.

### What if I am locked out and cannot use the dashboard?

Run `wp bettershield recover`. It turns on safe mode so the standard sign-in page answers again, and it needs no user.

### How do I stop BetterShield flagging files my host deployed?

Record the deployment instead of ignoring the report. `integrity record-update` records a known plugin or theme update made outside WordPress’s updater, and `integrity expect-file` accepts one reviewed file your host wrote. Both need `--user=<login>`, and both leave other open changes for you to review.

## Conclusion

The commands are the dashboard’s own code with a terminal in front: read freely, change with a named user, preview before you apply, and keep `recover` in your back pocket. BetterShield is free on [WordPress.org](https://wordpress.org/plugins/bettershield/); every option is listed in the [WP-CLI commands reference](/docs/wp-cli-commands/), and the fixes behind `harden` are explained in [Hardening](/docs/hardening/).
