Skip to content

Turn on the web application firewall for a site

Each hosting has its own web application firewall (WAF): a set of nginx rules that refuse a request with a 403 before PHP sees it. You configure it on the hosting’s Protection card, and the refusals show up under WAF activity on the same tab.

A site whose level nobody set is Off. Hyperion refuses nothing on it until you pick a level.

  • The hosting is not a reverse proxy or a redirect. Those two carry none of these rules, and their Protection card says so. Protect the upstream application or the target site instead.
  • You can edit the hosting’s configuration. Installing the OWASP Core Rule Set engine also needs the Administrator or Owner role.
  • To block countries, install the GeoIP database first. It is held on the master, so only sites on the master can block countries. See Install the GeoIP database.
  1. Open Hostings in the sidebar, click the site, then open the Settings & limits tab.
  2. Scroll to the Protection group and find the Web application firewall section.
  3. Pick a Level:
    • Off: no WAF rules.
    • Standard (recommended): the rules marked Standard below.
    • Strict: Standard plus the rules marked Strict.
  4. Click Save protection.

Hyperion validates the new vhost with nginx -t before it commits it. If the check fails, the previous vhost stays active and you see the error.

Rule Level What it refuses Counts toward a ban
Attack patterns in the query string Standard Path traversal, SQL injection, remote includes and <script> in the URL’s query string Yes
Vulnerability scanners Standard Requests announcing a known scanner: nikto, sqlmap, wpscan, masscan, zgrab and similar Yes
XML-RPC (xmlrpc.php) Standard xmlrpc.php. Jetpack and the WordPress mobile app still use it, so turn this rule off if the site relies on them No
Sensitive WordPress files Standard wp-config.php, readme.html and license.txt Yes
Backups, dumps and logs Standard Files ending in .sql, .bak, .old, .orig, .save, .swp, .tar, .gz, .tgz, .zip, .log, .ini or .sh No
PHP in uploads and cache Standard Running a .php file from wp-content/uploads or wp-content/cache. Shown only on sites that run PHP No
Repository and secret files Strict Probes for .env, .git, .svn, .hg and .DS_Store Yes
Author enumeration Strict ?author=<number> on the public site No
REST API user list Strict /wp-json/wp/v2/users for visitors who are not logged in No
Unusual HTTP methods Strict Anything other than GET, HEAD, POST, PUT, PATCH, DELETE and OPTIONS No
Requests without a User-Agent Strict Requests with no User-Agent header. Some uptime monitors and home-made scripts send none, so check yours first No

Use this when one rule refuses legitimate traffic, or when you want a Strict rule without the rest of Strict.

  1. Under Level, open Individual rules. It opens by itself when a rule is already pinned.
  2. For the rule, choose Follow the level, Always on or Always off. The pill beside the rule name shows on or off for what nginx renders now.
  3. Click Save protection.

A hosting set to Off with rules pinned on still refuses those requests. The header pill then reads “WAF off · N rules pinned on”.

Restrict the WordPress admin to your addresses

Section titled “Restrict the WordPress admin to your addresses”
  1. Under Admin & sign-ups, enter one address or network per line in Restrict /wp-admin and /wp-login.php to these IPs, for example 203.0.113.5 or 198.51.100.0/24. The field holds up to 2048 characters. Empty leaves the admin open.
  2. Click Save protection. The card header shows the pill admin IP lock.

Two switches in Admin & sign-ups add nginx rate limits. Both answer 429 past the limit.

  • Rate-limit public WordPress sign-ups caps registrations for the whole site at 10 requests per minute. It limits the site, not the caller, so during a flood it turns away genuine sign-ups too. If the site does not need public registration, close it on the WordPress tab instead.
  • Rate-limit PHP requests per visitor allows about 10 PHP requests per second per address, with a burst of 40. Logged-in WordPress users are exempt, and images, styles and scripts are not counted. Leave it off unless bots are the problem: many people behind one address, such as an office or a school, count as one visitor.
  1. Under Bots, switch on the families to refuse with a 403: AI scrapers, Social previews (blocking these stops link previews from rendering), SEO crawlers and Price comparison. Search engines are not listed, and nothing on this card can deindex the site.
  2. Under Countries, enter two-letter ISO codes separated by commas in Blocked countries, for example RU, CN, KP. This blocks the people in those countries as well as their bots: travellers, VPN users and mobile carriers routed abroad also get the 403.
  3. Click Save protection.

Until the GeoIP database is installed every visitor resolves to ZZ and no country rule matches anybody.

The Core Rule Set (CRS) runs ModSecurity on every request, bodies and headers included. It costs roughly 20 to 50 MB of memory per nginx worker while any site on the node uses it, plus a few milliseconds per request. The OWASP Core Rule Set (ModSecurity) section is further down the Protection card.

  1. If the section says ModSecurity is not installed, an Administrator or Owner clicks Install ModSecurity + OWASP CRS on this node and confirms. A background job installs the Debian packages libnginx-mod-http-modsecurity and modsecurity-crs on the node that serves the site, then checks that nginx still loads its configuration. Other roles see “ModSecurity is not installed on this site’s node. Ask an administrator to install it.”
  2. Set Mode to Detection only first. It runs every rule and records what it would refuse under WAF activity, refusing nothing. Run it for a few days of real traffic.
  3. Leave Paranoia level at 1 — standard. 2 — stricter catches more and refuses more legitimate requests.
  4. Choose an Anomaly threshold: 5 — standard refuses on a single critical match, 10 — relaxed and 20 — lenient need several rules to agree.
  5. Keep WordPress exclusions on for WordPress sites. It loads the CRS’s own exclusion set for editing posts, the customizer, comments and the REST API.
  6. Click Save rule set.
  7. When the recorded detections look right, set Mode to Blocking and save again. CRS refusals never count toward a firewall ban.

When a legitimate request is refused, click Allow this on its WAF activity entry. For a CRS entry it adds a rule exclusion for exactly the rules it matched, on its path only. The Rule exclusions table lists them with a Remove button, and the Add exclusion form takes Rule ids (for example 942100 941160) and Only for paths starting with (empty means the whole site).

The WAF activity card, below the Protection card, lists:

  • the count of requests blocked in the last 24 hours, and “would-block” detections from CRS detection mode;
  • Automatic bans, with Stop for this site or Turn on;
  • By rule: hits per rule over 24 hours and 7 days;
  • Latest refusals: time, address, rule and request, with Allow this when the rule can be turned off.

Allow this on a built-in rule pins that rule Always off for the whole site, not only for that request. Confirm the dialog, and Hyperion re-validates the vhost with nginx -t. To undo it, set the rule back to Follow the level under Individual rules.

Repeat offenders of the rules marked “Yes” above can earn a firewall ban. See Review, add and lift IP bans and Tune automatic bans.

  • The Protection card header shows WAF standard or WAF strict in the pill. The Security card on the Overview tab shows the same pill under WAF & wp-admin lock.
  • Request a path a rule covers and read the status code:
Terminal window
curl -s -o /dev/null -w '%{http_code}\n' 'https://example.com/wp-config.php'

Expected: 403.

  • The refusal appears under Latest refusals with the rule’s name.
  • The cluster-wide Protection page counts the site under Sites with the WAF on. See Check what the WAF refused.
  • Save protection shows an nginx error: the previous vhost stays active. Read the error, fix the offending value (an address that is not an IP or CIDR is the usual one), and save again.
  • Legitimate requests get a 403: open Latest refusals, find the rule, and click Allow this, or set it to Always off under Individual rules.
  • Country blocking changes nothing: the GeoIP database is not installed, or the site is on a worker node. See Install the GeoIP database.
  • “This site’s node did not report a ModSecurity engine — it probably runs an older Hyperion. Update the node to use the Core Rule Set.”: update the node, then reload the page.
  • The WAF activity card says “Automatic banning is switched off for this whole node”: the brute-force scanner is off. Refusals are still recorded. Turn the scanner on under Settings, Brute-force & GeoIP.