2026年11月12日
2026年11月12日
How to Add a WAF to WordPress: Cloudflare, ModSecurity or Wordfence
This article is a localized edition of the Japanese original. Read the Japanese original
A WAF (Web Application Firewall) inspects the content of requests to a website and blocks attacks such as SQL injection, cross-site scripting (XSS) and attempts to exploit known vulnerabilities. For WordPress, it helps reduce attacks in the window between the disclosure of a plugin or theme vulnerability and your update.
WAFs fall into three types depending on where they run. This article covers the characteristics and setup of each, together with login attempt limits that you should configure alongside. The scope of each service and the package versions were checked against the official documentation as of November 2026.
Three types of WAF
| Type | Example | Where requests are inspected | Suited to |
|---|---|---|---|
| Cloud | Cloudflare | Before they reach your server (DNS points to the cloud service) | Any environment. Adds no load to your server |
| Server | ModSecurity + OWASP CRS | Inside the web server (Apache, Nginx) | Environments where you can change the web server configuration, such as a VPS or dedicated server |
| Plugin | Wordfence | Inside WordPress (PHP) | Environments where you can't change the server configuration, such as shared hosting |
You can stack more than one, but running the same kind of inspection twice makes it harder to track down the cause of false positives. We recommend choosing one first, tuning it while watching the logs, and adding another only if needed.
Many hosting companies also offer a WAF that you can enable in their control panel. If your host has this feature, enabling it first is the easy option.
Method 1: Cloudflare (cloud)
With Cloudflare, every request to your site passes through Cloudflare before it reaches your server. WAF rules are configured in the Cloudflare dashboard.
The managed rulesets (rulesets provided by Cloudflare) available on each plan are:
| Ruleset | Free | Pro / Business / Enterprise |
|---|---|---|
| Free Managed Ruleset | Yes | Yes |
| Cloudflare Managed Ruleset | No | Yes |
| OWASP Core Ruleset | No | Yes |
The Free Managed Ruleset on the Free plan focuses on high-impact, widely exploited vulnerabilities. To use the Cloudflare Managed Ruleset, which includes rules for WordPress, you need the Pro plan or higher.
Points to watch when you set it up:
- Block direct access to the server: If your server's IP address is known, attackers can bypass Cloudflare and hit it directly. Restricting HTTP/HTTPS connections in the server firewall to Cloudflare's IP addresses makes the WAF harder to bypass.
- Logging visitor IPs: For requests through Cloudflare, your server logs record Cloudflare's IP addresses. To log visitor IPs, configure the web server to trust Cloudflare's IP ranges.
The initial setup is explained in How to set up Cloudflare for WordPress, and WAF rule settings in Cloudflare WAF settings.
Method 2: ModSecurity and OWASP CRS (server, Apache)
ModSecurity is a WAF engine that you build into Apache or Nginx. For the rules that detect attacks, you use the Core Rule Set (CRS) published by OWASP. The steps below are for Apache on Ubuntu 24.04 (the CRS in the Ubuntu 24.04 package is 3.3.5).
1. Install
sudo apt update
sudo apt install libapache2-mod-security2 modsecurity-crs
sudo a2enmod security2
The CRS configuration file is /etc/modsecurity/crs/crs-setup.conf, and the rules themselves are in /usr/share/modsecurity-crs/rules/.
2. Start in detection-only mode
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
grep -n "^SecRuleEngine" /etc/modsecurity/modsecurity.conf
# Confirm that it says SecRuleEngine DetectionOnly
DetectionOnly only logs requests judged to be attacks; it doesn't block them. If you enable blocking right away, saving posts in the admin screen or working with plugins can stop working, so run in this mode first.
3. Enable the WordPress rule exclusions
CRS 3.x includes rule exclusions that keep normal WordPress operations from being flagged. In /etc/modsecurity/crs/crs-setup.conf, find the id:900130 section, remove the leading # and keep only the WordPress line.
SecAction \
"id:900130,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.crs_exclusions_wordpress=1"
Check the configuration, then restart Apache.
sudo apache2ctl configtest
sudo systemctl restart apache2
4. Test detection and check the logs
Request your own site with a string that looks like SQL injection.
curl -s -o /dev/null -w "%{http_code}\n" "https://example.com/?id=1%27%20OR%20%271%27=%271"
In DetectionOnly mode this request returns 200 or similar as usual, but entries starting with ModSecurity: Warning are written to the Apache error log.
sudo grep "ModSecurity" /var/log/apache2/error.log | tail -n 20
Details go to the audit log. You can find its location in SecAuditLog in /etc/modsecurity/modsecurity.conf.
5. Check for false positives, then switch to blocking
For a few days to a week, use the admin screen as usual (saving posts, uploading media, updating plugins and so on) and submit your forms, then check whether normal operations show up in the log. If there are no problems, enable blocking.
sudo sed -i 's/^SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf
sudo apache2ctl configtest && sudo systemctl restart apache2
Once blocking is enabled, requests judged to be attacks receive a 403. Confirm that the curl from step 4 returns 403. If you find false positives, use the rule ID in the log to exclude only that URL or parameter.
For detailed ModSecurity configuration, see How to use ModSecurity with Apache; for Nginx, see How to use ModSecurity with Nginx.
Method 3: Wordfence (plugin)
On shared hosting where you can't change the server configuration, use a plugin with a WAF. Wordfence, a well-known example, includes the firewall in its free version.
- Protection levels: With "Basic WordPress Protection", the firewall runs as WordPress loads. "Extended Protection" uses PHP's
auto_prepend_filesetting to run the firewall before WordPress and other plugins. Both are available in the free version. To switch to Extended Protection, follow the steps shown in the Wordfence screen. Some servers require editing.htaccessor.user.ini. - Learning mode: For the first week after installation, the firewall runs in learning mode, learning your site's normal requests to reduce false positives. Note that firewall rules don't block anything during this period.
- Rule updates: Firewall rules for the free version are delivered 30 days after their release to the paid version.
A plugin WAF runs on the same server as WordPress, so heavy attack traffic increases server load. For high-traffic sites, consider combining it with a cloud or server WAF.
Also set up: limit login attempts
WAF rules can't stop every attack that tries passwords by brute force. Also limit the number of attempts against wp-login.php.
If you use Nginx, limit_req limits how often a single IP address can send requests. This example is for Ubuntu 24.04 with PHP-FPM 8.3.
# /etc/nginx/conf.d/wp-limit.conf (a location included in the http context)
limit_req_zone $binary_remote_addr zone=wp_login:10m rate=5r/m;
# Inside the site's server block
location = /wp-login.php {
limit_req zone=wp_login burst=5 nodelay;
limit_req_status 429;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
# Close it only if you don't use XML-RPC
location = /xmlrpc.php {
deny all;
}
rate=5r/mmeans 5 requests per minute, andburst=5is how many may temporarily exceed it. Requests beyond that receive the 429 set inlimit_req_status.- The socket path in
fastcgi_passdepends on your PHP version and environment. Match the value in your existinglocation ~ \.php$block. - If you use features that rely on XML-RPC, such as Jetpack or the official WordPress mobile apps, don't close
xmlrpc.php.
After the change, check the syntax with sudo nginx -t and apply it with sudo systemctl reload nginx. On Apache or shared hosting, you can limit login attempts with a plugin. For details, see How to limit login attempts.
Operating tips
- Start in detection mode and tune using the logs: With any type of WAF, false positives can make saving posts or submitting forms fail. Right after setup, run in detection-only mode (or learning mode), check the logs, then enable blocking.
- Don't write your own detection code in functions.php: Home-made checks that scan request strings with regular expressions tend to block legitimate post content and comments while letting attacks slip through. Maintained rulesets (CRS, Cloudflare, Wordfence) are the safer choice.
- A WAF is not a substitute for updates: A WAF reduces attacks; it doesn't fix the vulnerability itself. Keep WordPress core, plugins and themes updated.
- Set security headers separately: HTTP headers such as clickjacking protection are configured on the web server, separately from the WAF. See How to set security headers.
Summary
- There are three types of WAF: cloud (Cloudflare), server (ModSecurity + CRS) and plugin (Wordfence). Choose based on whether you can change the server configuration and on your traffic.
- The managed ruleset available on Cloudflare's Free plan is the Free Managed Ruleset. Also block direct access to your server.
- Start ModSecurity in
DetectionOnly, enable the CRS WordPress exclusions, and switch toSecRuleEngine Ononly after the logs show no false positives. - Wordfence includes the firewall in its free version, and Extended Protection lets it inspect requests before WordPress loads.
- Configure login attempt limits separately from the WAF.
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)