# Server compatibility: what BetterShield needs from your host

The WordPress and PHP versions, PHP extensions, HTTPS, web server and scheduled tasks BetterShield needs, and a checklist to hand your host.

[BetterShield](/) runs on ordinary WordPress hosting. A few features depend on the server, and where one cannot work, its screen says why instead of claiming a protection the site does not have. This page lists what each feature needs, with a checklist for your host at the end.

## Requirements

| What | Needs |
|---|---|
| BetterShield | WordPress 6.7 or newer and PHP 8.0 or newer. Tested up to WordPress 7.1. |
| Assistants (the MCP server), agent abilities and BetterShield Hub | The WordPress Abilities API. WordPress 6.9 and newer include it; on 6.7 and 6.8, BetterShield loads its own copy. |
| AI explanations | WordPress 7.0 or newer, with an AI provider connected under **Settings › Connectors**. |
| Recording every ability call on **Activity › Agents** | WordPress 7.1 or newer. |

On PHP older than 8.0, BetterShield does not run and the dashboard says: "BetterShield needs PHP 8.0 or newer. This site runs PHP [version], so the plugin is not running. Nothing else on the site is affected."

If an Agents screen says **Requires WordPress 6.9.**, or the Hub card says **Connecting needs WordPress 6.9 or later.**, BetterShield's own copy of the Abilities API is missing. Reinstall BetterShield from WordPress.org, or update WordPress.

## PHP extensions

| Extension | What it is for | Without it |
|---|---|---|
| OpenSSL | Checking a passkey's signature, and reading when the site's certificate expires. | Passkeys stay off, with "This server is missing the OpenSSL extension, which is needed to check a passkey signature." The certificate check cannot read the date. |
| Sodium | Passkeys of the Ed25519 type. | Devices are offered the other passkey types only. |
| Zip | Packing the kept copies of replaced files into one archive when you delete the plugin with that choice. | WordPress's own zip writer is used instead. |

The daily check of your domain's SPF, DMARC and CAA records needs PHP's DNS lookups, and the low disk space warning needs PHP to report free space. Where a host disables either, that check cannot run, and a check that could not run is never reported as passed.

## HTTPS

| Feature | Needs | Otherwise |
|---|---|---|
| Passkeys | HTTPS, or localhost | Switched off: "Passkeys need HTTPS." |
| **Ask for a sign-in puzzle after wrong passwords** | HTTPS, or localhost | "Needs HTTPS. Your site isn’t using HTTPS yet, so this is off." Sign-ins on plain HTTP skip the check. |
| **Strict HTTPS (HSTS)** | A site address that is already HTTPS | Not offered. A certificate close to expiry is flagged before you apply it. |
| An assistant connecting by address | https | **Connection health** shows **Served over https** as no. |

## Apache, LiteSpeed, nginx and IIS

Two fixes work by writing .htaccess rules: **Uploads code block** (in the uploads folder) and **Private file protection** (in the site's root). BetterShield reads the server type from what the server tells PHP.

| Server | Reads .htaccess | Those two fixes |
|---|---|---|
| Apache | Yes | Available. |
| LiteSpeed | Its paid edition reads these rules; the free OpenLiteSpeed reads only rewrite rules, and the two report themselves the same way | Available, with "The rules will be written, but whether they are enforced could not be confirmed from here." |
| OpenLiteSpeed (when it names itself) | Only rewrite rules | **Not available here**: "This site runs on OpenLiteSpeed, which reads only rewrite rules from .htaccess files and skips the kind this fix writes, so it cannot work here." |
| nginx, IIS | No | **Not available here**: "This site runs on nginx, which does not read .htaccess files, so this fix cannot work here." |
| Not identified | Cannot tell | Applied, with "This server did not identify itself as one that reads .htaccess files. The rules will be written, but whether they are enforced could not be confirmed from here." |

On nginx, IIS or OpenLiteSpeed, your host can add the same rules to the server configuration. Every other fix needs no server rules: **Folder listing protection** places blank index files instead, and works on every server. The **Publish a security contact** setting on **Settings › General** works everywhere too, because WordPress serves the file rather than writing it.

When BetterShield puts a changed file back, it keeps the replaced copy in `wp-content/uploads/bettershield-quarantine/` and seals that folder with an .htaccess rule. On nginx, IIS or OpenLiteSpeed the rule does nothing, and the finding **Files in quarantine can still be opened from the web** (Medium) asks you to deny the folder in your server configuration. Nothing on your site loads from it.

If the site address and WordPress address point somewhere other than where the files are (an alias, or a rewrite in front of the whole site), the sensitive files fix refuses with "This plugin could not work out which .htaccess file governs your site, so nothing was written."

## wp-config.php is never edited

BetterShield never writes to wp-config.php.

- **File editor lock** works through WordPress's permissions. If wp-config.php already turns the editor off, the fix says so.
- **Sign everyone out on demand** mixes a secret of its own into the keys WordPress uses, and leaves the keys in wp-config.php as they are.
- Findings about wp-config.php itself, such as **Your site’s security keys are missing or weak** or **Error messages are visible to visitors**, are changes you make.

## Proxies, CDNs and load balancers

Sign-in lockouts need to tell visitors apart. Cloudflare is recognized from its published address ranges and needs no **Addresses of your CDN or proxy** entry, but press **Yes, this site is behind Cloudflare** when the screen asks: until then, a lockout counts Cloudflare's address and the visitor's together. Name any other reverse proxy or load balancer under **Addresses of your CDN or proxy** and have it send X-Forwarded-For. Until you do, a proxy on a private address makes lockouts stand down (the finding **Failed sign-ins are not being counted, so password guessing is not stopped** says so), and a proxy on a public address is counted as one visitor, so a lockout would shut out everyone behind it. With Cloudflare in front of a load balancer on a private address, name the load balancer, and Cloudflare is recognized behind it. The public form limits record those visitors but never close a form to them. See [Proxies and Cloudflare](/docs/proxies-and-cloudflare/).

### HTTPS that ends at a proxy

When a proxy or load balancer handles the encryption without telling WordPress, WordPress sees plain HTTP even though the site's address is HTTPS, and the HSTS header, which is only sent on requests WordPress sees as encrypted, never goes out. **Strict HTTPS (HSTS)** warns that "applying this changes nothing" until wp-config.php sets the HTTPS server variable for those requests. Passkeys go by the site's address, so they are still offered.

### Page caches and the sign-in page

A cached sign-in page hands every visitor a copy built for someone else, which can refuse a correct password. The finding **Your sign-in page is being cached** (Medium) checks for this daily. Exclude the sign-in address from your cache plugin, your host's cache and your CDN. If you move the sign-in address while WP Rocket, W3 Total Cache, WP Super Cache or LiteSpeed Cache is active, the fix reminds you to exclude the new address too.

## Server firewall

On a server that runs Fail2Ban, **Send lockouts to the server’s firewall**, on the **Server firewall** card on **Protection › Login & Access**, writes one line to the server's auth log each time sign-in or a form pauses a connection, so the firewall can block that network before it reaches WordPress. The line names the network (IPv4 /24, IPv6 /64), never an address or a username, and a network that holds an address on your allowed list writes no line. A sample Fail2Ban filter and jail ship in the plugin's `fail2ban` folder; only a jail written for this line reads it. A host can turn it on for every site with the `bettershield_host_ban_line` filter, and the card then says it is set by your host. Where PHP's system log functions are turned off, it is unavailable. On multisite it is set from the main site. See [Server firewall](/docs/server-firewall/).

## Replaced session storage

If another plugin has replaced how WordPress stores sessions, **Sessions per account** on Login & Access is unavailable: "Not available on this site: something has replaced how WordPress stores sessions, and a limit cannot be applied to them." Signing an account out of everything still works.

## Scheduled tasks (WP-Cron)

BetterShield's background work runs on WordPress's scheduled tasks: hourly (the hardening monitor, incidents, file check steps, unusual-activity notes), daily (the audit, the log seal, the certificate, domain and sign-in page checks, housekeeping) and weekly (looking for files a stranger could download). Anything that asks your own server a question runs from the schedule only, never inside a visitor's page view.

WordPress runs scheduled tasks when page loads trigger them, so a quiet site can miss them. When a task is badly overdue, BetterShield runs it, or asks the site to run it, the next time an administrator opens the dashboard. If whole cycles are still missed, the finding **BetterShield’s background jobs have stopped running** (Medium) appears. The fix is on the host: a system cron that runs wp-cron.php, or `wp cron event run --due-now`, every few minutes. We recommend one on every site you look after.

With the **Hub link** on, sending security events to BetterShield Hub is scheduled work too, every five minutes. See [Connect to BetterShield Hub](/docs/connect-to-bettershield-hub/).

## Multisite

Network activation sets up every site, and a site created later is set up the same way. Each site keeps its own settings, counts and findings. **Private file protection** is applied from the main site, because every site shares one .htaccess, and sign-in key rotation is done from the network dashboard. See [Multisite networks](/docs/multisite-network/).

## A checklist for your host

- PHP 8.0 or newer, with OpenSSL and Sodium enabled.
- HTTPS on the site's address. If encryption ends at a proxy or load balancer, wp-config.php sets HTTPS for the requests it forwards.
- A proxy or load balancer in front of the site sends X-Forwarded-For, and you have its address range to add under **Addresses of your CDN or proxy**.
- The sign-in page, and any custom sign-in address, excluded from page caching and CDN caching.
- A system cron running WordPress's scheduled tasks every few minutes.
- If the server runs Fail2Ban and you want paused networks blocked at the firewall: a jail that reads BetterShield's auth log line (a sample filter and jail ship in the plugin's `fail2ban` folder).
- Outbound HTTPS to WordPress.org, for the file check. See [What BetterShield contacts](/docs/what-bettershield-contacts/).
- The site may make requests to its own address.
- WordPress can send email, for alerts and the recovery link.
- The database account may create tables, which BetterShield does on activation and updates.
- WordPress can write to the uploads folder and, on Apache or LiteSpeed, the site's .htaccess.
- On nginx, IIS or OpenLiteSpeed: the rules for the two .htaccess fixes in the server configuration, and web access denied to `wp-content/uploads/bettershield-quarantine/`.

## Related

- [Troubleshooting](/docs/troubleshooting/)
- [Proxies and Cloudflare](/docs/proxies-and-cloudflare/)
- [Hardening](/docs/hardening/)
- [What BetterShield contacts](/docs/what-bettershield-contacts/)
- [Multisite networks](/docs/multisite-network/)
- [Getting help](/docs/getting-help/)
