WordPress Security

Tips to lock down your WordPress site (and non WordPress sites)

WordPress Admin User

Do not use the default admin user. If you have, create another user with admin rights and delete the admin user.

On the delete, you can transfer all posts to the new user.

WordPress Fail2Ban plugin

If you have fail2ban installed


  1. Upload the plugin to your plugins directory
  2. Activate the plugin through the ‘Plugins’ menu in WordPress
  3. Copy wordpress.conf to your fail2ban/filters.d directory. You’ll find this in./wp-content/plugins/wp-fail2ban/wordpress.conf
  4. Edit jail.local to include something like:[wordpress] enabled = true filter = wordpress logpath = /var/log/auth.log
  5. Reload or restart fail2ban

Local to WordPress





See this article at http://thehackernews.com/2015/01/ghost-linux-security-vulnerability_29.html

If not used, disable this. Newer versions of WordPress have this enabled and no setting to disable it. Plugins can do this for you.

Here’s some that will do this https://wordpress.org/plugins/disable-xml-rpc-pingback/ (this one disables some methods) or https://wordpress.org/plugins/disable-xml-rpc/ (this one disables it all together)

Two Factor Authentication

Setting up two factor authentication will also tighten your security.

Have a look at this plugin https://wordpress.org/plugins/two-factor-authentication/

“Secure WordPress login with this two factor authentication (TFA) plugin. Users for whom it is enabled will require a one-time code in order to log in.”

You can find it by searching for Two Factor Authentication in the install new plugin action.

In case you lock yourself out you can add this to the start of your wp-config.php file: define(‘TWO_FACTOR_DISABLE’, true);

You’ll need an autheticator application or browser plugin to generate the required code.

See https://chrome.google.com/webstore/search/authenticator for Chrome plugins that will do this.

I’ve used the Authenticator offered by sneezry.com

Plug in your secret key provided by the wordpress plugin, and give it a friendly (Account) name. Choose Time Based.

Note: For time based it is critical your server and application clocks are synchronized.

Local to Apache Server

Mod Security

mod_security is the most effective according to some security research done but has a higher number of false positives and will require monitoring and configuration to eliminate the false positives. You can accomplish this with, for example, a disable.conf file in your /etc/modsecurity/ directory or included in your apache site configuration file with content such as this:

<LocationMatch "/dashboard/management.php">
SecRuleRemoveById 214460
SecRuleRemoveById 214940
apt-get install libapache2-modsecurity

See https://www.digitalocean.com/community/tutorials/how-to-set-up-mod_security-with-apache-on-debian-ubuntu for tips on setting this up

OWASP has a free rule set for modsecurity.  https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project

Comodo is now providing free modsecurity rules based on the OWASP set. See https://waf.comodo.com/ At time of writing apache version 2 was not supported for their automatic installer. You’ll need to do this manually.

You can use mod_security to lock down login by including this configuration (create a wordpress.conf file in /etc/modsecurity). See https://smyl.es/how-to-block-wp-login-php-brute-logins-with-cpanel-mod-security-and-configserver-firewall/

SecUploadDir /tmp
SecTmpDir /tmp
SecDataDir /tmp
SecRequestBodyAccess On
SecAction phase:1,nolog,pass,initcol:ip=%{REMOTE_ADDR},initcol:user=%{REMOTE_ADDR},id:5000134
<Locationmatch "/wp-login.php">
 # Setup brute force detection.
# React if block flag has been set.
 SecRule user:bf_block "@gt 0" "deny,status:401,log,id:5000135,msg:'ip address blocked for 5 minutes, more than 10 login attempts in 3 minutes.'"
# Setup Tracking. On a successful login, a 302 redirect is performed, a 200 indicates login failed.
 SecRule RESPONSE_STATUS "^302" "phase:5,t:none,nolog,pass,setvar:ip.bf_counter=0,id:5000136"
 SecRule RESPONSE_STATUS "^200" "phase:5,chain,t:none,nolog,pass,setvar:ip.bf_counter=+1,deprecatevar:ip.bf_counter=1/180,id:5000137"
# @gt 10 means #failed attempts, user.cf.block=300 means block for 10 minutes
 SecRule ip:bf_counter "@gt 10" "t:none,setvar:user.bf_block=1,expirevar:user.bf_block=300,setvar:ip.bf_counter=0"
ErrorDocument 401 default

A simpler version:
# DEPRECATE bf_block by 20 every 120 seconds
SecRule REQUEST_URI "@contains wp-login.php" \
SecRule IP:bf_block "@gt 3" \
 "log,deny,status:403,phase:1,msg:'wordpress.conf: Possible Brute Force Attack',id:5000136"

If you have a need to bypass modsecurity for a particular IP
SecRule REMOTE_ADDR "@ipMatch" "id:26091975,phase:1,pass,nolog,allow,ctl:ruleEngine=Off"
or load from a file 
SecRule REMOTE_ADDR "@ipMatchFromFile ips.txt" "id:26091975,phase:1,pass,nolog,allow,ctl:ruleEngine=Off"
where ips.txt contains a list of addresses in CIDR format.
If you still want to log it use ruleEngine=DetectionOnly

To do this via apache insert a line in your site configuration or an .htaccess file like
SetEnvIfNoCase Remote_Addr ^$ MODSEC_ENABLE=Off

mod Evasive

mod_evasive can provide DDoS protection.

mod_evasive is an evasive maneuvers module for Apache to provide evasive action in the event of an HTTP DoS or DDoS attack or brute force attack. It is also designed to be a detection and network management tool, and can be easily configured to talk to ipchains, firewalls, routers, and etcetera. mod_evasive presently reports abuses via email and syslog facilities.

mod Geoip

If you are looking at blocking countries from all the sites on your host, use a firewall such as CSF.

To lockout countries from a particular site, within the Apache virtual host directory include mod_geoip directives specifying the countries to block.

You’ll need to download the geopip files and install libapache2-mod-geoip.

Ex. apt-get install libapache2-mod-geoip

This example relies on you downloading the geoip files to /usr/share/GeoIP.

Edit the configuration file /etc/apache2/mods-enabled/geoip.conf and activate the module and change the path of the database. (GeoLiteCity.dat also returns the CityNames and the geographic locations):

LoadModule geoip_module modules/mod_geoip.so
<IfModule mod_geoip.c>
  GeoIPEnable On
  GeoIPDBFile /usr/share/GeoIP/GeoIP.dat
  GeoIPScanProxyHeaders On

and restart Apache:

/ect/init.d/apache restart

Now create a .htaccess file or include in your virtual server. For example if you want to block clients from Russia and China:

Deny from env=BlockCountry

If you want to redirect based on the country using mod_rewrite in combination with mod_geoip, your .htaccess file or virtual server could look like this:

RewriteEngine on
RewriteRule ^(.*)$ http://www.mydomain.com/nl/$1 [L]

ConfigServer (CSF)

This is an excellent (and free) firewall. See http://configserver.com/

It has the ability to monitor various logs as well and provides hooks into mod_security alerts to block IPs. At time of writing, the current version is limited to a single apache error log for monitoring standard errors and modsecurity triggers. For multiple virtual servers, make them all use the same /var/log/apache2/error.log file.

Apache Page Lockdown

There are increasing attacks to WordPress sites trying brute force login. You’ll see this in your apache log where multiple attempts are made to wp-login.php from the same IP. You can create mod_security rules etc. to try to stop this but I’ve found the simplest is to lock down wp-login.php file via your apache configuration or via .htaccess. This works great if you are the only poster to site. With multiple authors you’ll need to add their ids and passwords to the Apache authorization file. This technique ends up creating an additional authentication that is separate from the usual WordPress login.

Something like this:

# Protect wp-login
<Files wp-login.php>
AuthUserFile ~/.htpasswd
AuthName “Private access”
AuthType Basic
require user mysecretuser

Ninja Firewall

Ninja firewall provides PHP protection not only for WordPress but also any PHP website. The Pro edition can be installed stand alone and through the use of an .htaccess file, PHP directed to filter all calls via its firewall. There is also a WordPress edition if you are interested in only protecting WordPress.

Use open_basedir

In your virtual server assign open_basedir to the directory your site is in. This stops attempts at opening files outside your site.

Ex. php_admin_value open_basedir PATHTOYOURSITE

You can turn this off with php_admin_value open_basedir none

If your site uses multiple directories (Moodle and Owncloud   use a site directory and a data directory) these can be specified as: open_basedir  “/path/to/first/folder:/path/to/second/folder”


External Services



Cloudfare requires you to repoint to their DNS service.

No limit on traffic.

SSL is not supported on the free plan

Seen some reviews talking about slowness and outages with their service. Some security testing results have shown WAF doesn’t work.

Since CloudFlare acts as a reverse proxy for web properties, all connections come from a CloudFlare IP. CloudFlare passes the originating IP in the X-Forwarded-For header. However, CloudFlare’s IPs will appear in your server logs until you install mod_cloudflare, which is for Apache, or an equivalent solution on your server. We have several solutions based on what type of server you are using:



Incapsula does not require you to repoint to their DNS service, but rather change the IP address to their’s for protected sites. Discussions have also mentioned Incapsula does a better job a security.

There’s a 50GB/month bandwith limitation on the free Incapsula account

SSL is not supported on the free plan

An issue that often comes up for users of any full proxy-based product is that the original client IP address is often lost to the application or web server. This is because in a full proxy system there are two connections; one between the client and the proxy, and a second one between the proxy and the web server. Essentially, the web server sees the connection as coming from the proxy (Incapsula), not the client.

Needless to say, this can cause problems if you want to know the IP address of the real client for logging, for troubleshooting, for tracking or performing IP address specific tasks such as geocoding.

Incapsula has developed solutions for common applications and development frameworks that can be used to restore the original client IPs. Visit this section for the list of available extensions http://support.incapsula.com/forums/20158871-extensions.

If you did not find an extension that matches your environment you can easily extract the original client IPs yourself. Incapsula inserts the original client IP address into two HTTP headers so it can be retrieved by the server for processing. The first is the standard HTTP header “X-Forwarded-For” and the second is an Incapsula header “Incap-Client-IP”.

For example, configuring Apache to use the X-Forwarded-For instead of (or in conjunction with) the normal HTTP client header is pretty simple. Open your configuration file (usually in /etc/httpd/conf/) and find the section describing the log formats. Then create a new one that includes this to extract the X-Forwarded-For value:

ex.  LogFormat “%{X-Forwarded-For}i %l %u %t \”%r\” %>s %O \”%{Referer}i\” \”%{User-Agent}i\”” forward_combined

and modify your logging to use the forward_combined format.

If you did not find an extension that matches your environment you can easily extract the original client IPs yourself. Incapsula inserts the original client IP address into two HTTP headers so it can be retrieved by the server for processing. The first is the standard HTTP header “X-Forwarded-For” and the second is an Incapsula header “Incap-Client-IP”.

For example, configuring Apache to use the X-Forwarded-For instead of (or in conjunction with) the normal HTTP client header is pretty simple. Open your configuration file (usually in /etc/httpd/conf/) and find the section describing the log formats. Then add the following to the log format you want to modify, or create a new one that includes this to extract the X-Forwarded-For value:


That’s it. If you don’t care about the proxy IP address, you can simply replace the traditional %h in the common log format with the new value, or you can add it as an additional header. Restart or reload Apache, and you’re ready to go.

Restart or reload Apache, and you’re ready to go.

Stopping Spam

Under Settings, Discussion, turn off “ “. This will apply to new posts, existing ones must be modified (Use Quick edit and turn off “ 

Some useful plugins Akismet, WP-SpamShield Anti-Spam, WP-SpamFree

Recent News from Hacker News