Skip to main content

Security

FREE!

A few measures that work, and no pretending about the rest.

Login throttling, two-factor sign-in, XML-RPC and header hardening, a custom login address, and checks in Site Health — each one off until you choose it, each one telling you what it does not do.

Most security plugins are judged by the length of their feature list, and many of the items on those lists stop nothing. Security ships only measures that stop a specific attack, plus checks that tell you the truth about settings a plugin cannot enforce. Every option says, in its own description, what it does not protect against — because a switch that implies more safety than it gives is how people skip the measures that matter.

It is off by default and has to be installed and activated on purpose, from Unyson+ → Extensions. After activation, only read-only checks run. Nothing about how people sign in changes until you switch a measure on. Settings live at Unyson+ → Security.

The Status tab listing each measure and whether it is on

What each measure does — and does not do​

MeasureStopsDoes not stop
Login throttlingPassword guessing from one place, on the login page, XML-RPC and application-password sign-insSlow guessing spread across many addresses (two-factor sign-in does)
Two-factor sign-inSigning in with a leaked, reused or guessed passwordA session that is already signed in; application passwords (by design)
One message for any wrong sign-inThe login form confirming which usernames existUsernames shown elsewhere — author pages, the lost-password form
XML-RPC: pingbacks off / blockYour site being used to send requests to other sitesMuch password guessing — WordPress already limits that on XML-RPC
Security headersClickjacking, content-type guessing, leaking full URLs to other sites; HSTS stops HTTPS being downgradedCross-site scripting — that needs a Content-Security-Policy, which is not auto-generated
Hide the user list from visitorsAutomated tools listing your usernamesUsernames that bylines and author pages already show — this is noise reduction
Custom login addressBots hammering wp-login.php — less log noise, less server workA determined attacker: it is obscurity, not protection
Log everyone outSomeone still using a stolen login cookieSomeone who has the password — pair it with password resets

Login throttling​

Login throttling settings

After too many wrong passwords from one address, that address waits before it may try again. The wait doubles each time within a day, up to a maximum you set, and is never permanent.

  • Counted per address, and per address + username — never per username alone. Otherwise anyone could lock you out of your own account by guessing badly on purpose.
  • While an address is cooling down, even the right password is refused. Letting it through would tell an attacker exactly when a guess was right.
  • Never throttle takes addresses or ranges (203.0.113.0/24) — useful for an office where many people share one address.
  • Current lockouts are listed on the Status tab, with an Unlock button. Addresses are shown with the last part hidden; the full address is never stored.

:::caution Behind a proxy or CDN? Then every visitor reaches WordPress from the same address — the proxy's — and one attacker would lock everyone out. Security detects this and refuses to switch throttling on until you tell it where the real visitor address comes from (Visitor addresses behind a proxy or CDN). A header is only believed when the request comes from a proxy you trust, because anyone can send one. :::

Two-factor sign-in​

Switch on Offer two-factor sign-in, then each person sets it up from their own profile (Users → Profile). After their password, they enter a 6-digit code from an authenticator app on their phone.

Setting up two-factor sign-in on the profile screen
  • The QR code is drawn in your browser. The key is never sent to another service.
  • Setup ends with ten recovery codes, shown once. Each works one time in place of an app code.
  • Turning it off, or making new recovery codes, needs a current code — so a session left open on a shared computer cannot remove your second factor.
  • An administrator can Reset two-factor sign-in for someone who has lost both their phone and their codes (on that user's edit screen).
The code step after the password

What it does not cover: application passwords (used by programs, not people) are not asked for a code — they are limited to what they were created for and can each be revoked. XML-RPC password sign-ins are refused for anyone who has two-factor sign-in on, so that door does not stay open.

By default the secrets are stored in the database unencrypted. To encrypt them, add a random key of 32 or more characters to wp-config.php before anyone sets up two-factor sign-in:

define( 'UPW_SECURITY_2FA_KEY', 'paste-a-long-random-string-here' );

Changing or removing that key later means existing users need a recovery code to get back in.

Hardening​

Security header settings
  • XML-RPC — Pingbacks off removes pingbacks and keeps the rest of XML-RPC. Block refuses every XML-RPC request; remote-publishing tools and older mobile apps that use it stop working. (Theme Settings → Misc → Performance has a Disable XML-RPC logins switch. It only turns off the methods that need a login — pingbacks keep working.)
  • Security headers — framing protection, no content-type guessing, and a referrer policy, on the front end (WordPress already sends the first and last on the login and admin screens). HSTS needs HTTPS and deserves care: if HTTPS ever stops working, returning visitors cannot reach the site until the duration runs out. Start with one day. Switching HSTS off tells browsers to forget it.
  • Hide the user list from visitors — for logged-out visitors, stops the REST API user list, ?author= links and embed data from listing usernames. A separate front end that reads the user list without signing in would lose it.

Headers sent by WordPress do not reach static files or page-cached pages. Your web server or CDN is the more reliable place for them; the Site Health check reports what visitors actually receive.

Custom login address​

Custom login address setting

Enter a name and the login page moves to https://your-site/that-name. wp-login.php and logged-out visits to /wp-admin/ then show your theme's 404 page. Every link WordPress generates — log out, lost password, the reset email, password-protected pages — follows automatically.

Be clear about what this buys. It cuts automated login attempts and log noise. It does not hide your site from anyone determined: the REST API, admin-ajax and XML-RPC stay reachable, and any login link on your site reveals the address. Throttling and two-factor sign-in are the protection.

Before a new address is saved it is checked: it cannot be a WordPress path, one of the first names bots try (login, admin…), or anything your site already uses — a page, a post in any status, a category, an archive. Then the site requests the new address itself, and if the login form does not appear, the previous address is put back. The new address is emailed to the site admin address.

It needs permalinks other than Plain, and it is not available on multisite yet.

If you are locked out​

None of these needs the admin screen:

SituationDo this
Anything Security does is in the wayAdd define( 'UPW_SECURITY_SAFE_MODE', true ); to wp-config.php — every measure is off until you remove it
Lost the custom login addressAdd define( 'UPW_SECURITY_LOGIN_SLUG', false ); to wp-config.php, or check the admin email
Locked out by throttlingWait it out, sign in from another address, or run wp upw-security unlock --all
Lost your phone and recovery codesAnother administrator resets it on your user screen, or run wp upw-security 2fa reset <user>
Want it gone entirelyDeactivate Security in Unyson+ → Extensions — WordPress is back to normal immediately, because nothing is written outside the extension's own settings

The command-line tools:

wp upw-security status
wp upw-security disable <throttle|errors|2fa|xmlrpc|headers|user-listing|login-url> # or --all
wp upw-security unlock --ip=<address> # or --all
wp upw-security 2fa reset <user>
wp upw-security login-url get | reset | set <name>
wp upw-security logout-all

Tools and checks​

The Tools box on the Status tab
  • Log everyone out — after a suspected break-in, ends every signed-in session at once, yours included.
  • Check whether PHP runs in uploads — writes a harmless test file into the uploads folder, requests it, and deletes it. If PHP runs there, an upload that slips past a check becomes code on your server; the result tells you how to turn it off for your web server.
  • Remove all security data — every setting, lockout and two-factor enrolment.

Tools → Site Health gains checks WordPress does not already run: the file editor constants, security keys, a debug log anyone can download, a browsable uploads folder, which security headers the home page actually sends, an unconfigured proxy, administrators without two-factor sign-in, and more. A check that cannot run says could not check — never a pass.

What Security deliberately does not do​

Some popular features are left out on purpose. A request "firewall" inside WordPress runs after the site has already loaded and would block the page builder's own saves, which legitimately contain HTML and scripts. A malware scanner on the same server an attacker controls can be edited by that attacker. Hiding the WordPress version, renaming the database prefix or moving wp-content stop nothing. And the extension never edits wp-config.php or .htaccess — measures that belong there are reported with the exact line to add, so switching Security off can never leave your site broken.