1.1 Sufix

On installation, always change the “wp_” sufix to something else. That will force atackers to know the names of the DB tables, instead of using default queries. “wp_x_” will create table names like “wp_x_options”

1.2 .htaccess

Add some of this lines for default protection:

# Disable indexing if index.html or php is not in a directory

Options -Indexes

# PROTECT WP-CONFIG
<Files wp-config.php>
        # Apache < 2.3
        <IfModule !mod_authz_core.c>
                Order allow,deny
                Deny from all
                Satisfy All
        </IfModule>
        # Apache >= 2.3
        <IfModule mod_authz_core.c>
                Require all denied
        </IfModule>
</Files>
# SECURE LOOSE FILES
# http://m0n.co/04
<IfModule mod_alias.c>
        RedirectMatch 403 (?i)(^#.*#|~)$
        RedirectMatch 403 (?i)/readme\.(html|txt)
        RedirectMatch 403 (?i)\.(ds_store|well-known)
        RedirectMatch 403 (?i)/wp-config-sample\.php
        RedirectMatch 403 (?i)\.(7z|bak|bz2|com|conf|dist|fla|git|inc|ini|log|old|psd|rar|ta$
</IfModule>

1.3 Remove version files

Add to the theme at the end of the functions.php file:

// remove version from head
remove_action('wp_head', 'wp_generator');

// remove version from rss
add_filter('the_generator', '__return_empty_string');

// remove version from scripts and styles
function shapeSpace_remove_version_scripts_styles($src) {
	if (strpos($src, 'ver=')) {
		$src = remove_query_arg('ver', $src);
	}
	return $src;
}
add_filter('style_loader_src', 'shapeSpace_remove_version_scripts_styles', 9999);
add_filter('script_loader_src', 'shapeSpace_remove_version_scripts_styles', 9999);	

1.4 Disable debug

By default is disabled, however if it was on…

At wp-config.php change the debug to false:

// Enable WP_DEBUG mode
define( 'WP_DEBUG', false );

// Enable Debug logging to the /wp-content/debug.log file
define( 'WP_DEBUG_LOG', false );
// Enable Debug display to the /wp-content/debug.log file
define( 'WP_DEBUG_DISPLAY', false );

1.5 Secure Logins attempts

Install Cerber for full control of login attempts.

1.6 Hide users enumeration

Change the user name to name and last name. To avoid the match of the user name and id.

Alternatively you can install Stop User enumeration

1.7 Keep an eye on the registered users

Install Activity log

1.8 Hotlinking…

Add this to the .htaccess to avoid people using your server for images:

# STOP HOTLINKING (METHOD 2)
<IfModule mod_rewrite.c>
	RewriteCond %{HTTP_REFERER} !^$
	RewriteCond %{HTTP_REFERER} !^http(s)?://([^.]+\.)?example\.com [NC]
	RewriteCond %{REQUEST_FILENAME} !hotlink.gif [NC]
	RewriteRule \.(gif|jpe?g?|png)$ /hotlink.gif [NC,R,L]
</IfModule>

That code will replace the image wanted for “/hotlink.gif”

1.9 Protecting the install file

Add this to the .htaccess to avoid access to it:

# STOP HOTLINKING (METHOD 2)
<IfModule mod_rewrite.c>
	RewriteCond %{HTTP_REFERER} !^$
	RewriteCond %{HTTP_REFERER} !^http(s)?://([^.]+\.)?example\.com [NC]
	RewriteCond %{REQUEST_FILENAME} !hotlink.gif [NC]
	RewriteRule \.(gif|jpe?g?|png)$ /hotlink.gif [NC,R,L]
</IfModule>

1.10 Stop automated spam

Add this to the .htaccess to avoid those f*****s:

# BLOCK SPAM
<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteCond %{REQUEST_METHOD} POST
	RewriteCond %{HTTP_USER_AGENT} ^$ [OR]
	RewriteCond %{HTTP_REFERER} !example.com [NC]
	RewriteCond %{REQUEST_URI} /wp-comments-post\.php [NC]
	RewriteRule .* - [F,L]
</IfModule>

1.11 Detect and block bad bots

Add this to the .htaccess to avoid those other f*****s:

# BLOCK BAD BOTS
<IfModule mod_rewrite.c>
	
	RewriteCond %{HTTP_USER_AGENT} (360Spider|acapbot|acoonbot|alexibot|asterias|attackbot|backdorbot|becomebot|binlar|blackwidow|blekkobot|blexbot|blowfish|bullseye|bunnys|butterfly|careerbot|casper|checkpriv|cheesebot|cherrypick|chinaclaw|choppy|clshttp|cmsworld|copernic|copyrightcheck|cosmos|crescent|cy_cho|datacha|demon|diavol|discobot|dittospyder|dotbot|dotnetdotcom|dumbot|emailcollector|emailsiphon|emailwolf|extract|eyenetie|feedfinder|flaming|flashget|flicky|foobot|g00g1e|getright|gigabot|go-ahead-got|gozilla|grabnet|grafula|harvest|heritrix|httrack|icarus6j|jetbot|jetcar|jikespider|kmccrew|leechftp|libweb|linkextractor|linkscan|linkwalker|loader|miner|majestic|mechanize|morfeus|moveoverbot|netmechanic|netspider|nicerspro|nikto|ninja|nutch|octopus|pagegrabber|planetwork|postrank|proximic|purebot|pycurl|python|queryn|queryseeker|radian6|radiation|realdownload|rogerbot|scooter|seekerspider|semalt|siclab|sindice|sistrix|sitebot|siteexplorer|sitesnagger|skygrid|smartdownload|snoopy|sosospider|spankbot|spbot|sqlmap|stackrambler|stripper|sucker|surftbot|sux0r|suzukacz|suzuran|takeout|teleport|telesoft|true_robots|turingos|turnit|vampire|vikspider|voideye|webleacher|webreaper|webstripper|webvac|webviewer|webwhacker|winhttp|wwwoffle|woxbot|xaldon|xxxyy|yamanalab|yioopbot|youda|zeus|zmeu|zune|zyborg) [NC]
	
	RewriteRule .* - [F,L]
	
</IfModule>

1.12 Firewall

Add this to the .htaccess to improve the security blocking proxy visits:

# 6G FIREWALL/BLACKLIST
# @ https://perishablepress.com/6g/

# 6G:[QUERY STRINGS]
<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteCond %{QUERY_STRING} (eval\() [NC,OR]
	RewriteCond %{QUERY_STRING} (127\.0\.0\.1) [NC,OR]
	RewriteCond %{QUERY_STRING} ([a-z0-9]{2000}) [NC,OR]
	RewriteCond %{QUERY_STRING} (javascript:)(.*)(;) [NC,OR]
	RewriteCond %{QUERY_STRING} (base64_encode)(.*)(\() [NC,OR]
	RewriteCond %{QUERY_STRING} (GLOBALS|REQUEST)(=|\[|%) [NC,OR]
	RewriteCond %{QUERY_STRING} (<|%3C)(.*)script(.*)(>|%3) [NC,OR]
	RewriteCond %{QUERY_STRING} (\\|\.\.\.|\.\./|~|`|<|>|\|) [NC,OR]
	RewriteCond %{QUERY_STRING} (boot\.ini|etc/passwd|self/environ) [NC,OR]
	RewriteCond %{QUERY_STRING} (thumbs?(_editor|open)?|tim(thumb)?)\.php [NC,OR]
	RewriteCond %{QUERY_STRING} (\'|\")(.*)(drop|insert|md5|select|union) [NC]
	RewriteRule .* - [F]
</IfModule>

# 6G:[REQUEST METHOD]
<IfModule mod_rewrite.c>
	RewriteCond %{REQUEST_METHOD} ^(connect|debug|delete|move|put|trace|track) [NC]
	RewriteRule .* - [F]
</IfModule>

# 6G:[REFERRERS]
<IfModule mod_rewrite.c>
	RewriteCond %{HTTP_REFERER} ([a-z0-9]{2000}) [NC,OR]
	RewriteCond %{HTTP_REFERER} (semalt.com|todaperfeita) [NC]
	RewriteRule .* - [F]
</IfModule>

# 6G:[REQUEST STRINGS]
<IfModule mod_alias.c>
	RedirectMatch 403 (?i)([a-z0-9]{2000})
	RedirectMatch 403 (?i)(https?|ftp|php):/
	RedirectMatch 403 (?i)(base64_encode)(.*)(\()
	RedirectMatch 403 (?i)(=\\\'|=\\%27|/\\\'/?)\.
	RedirectMatch 403 (?i)/(\$(\&)?|\*|\"|\.|,|&|&?)/?$
	RedirectMatch 403 (?i)(\{0\}|\(/\(|\.\.\.|\+\+\+|\\\"\\\")
	RedirectMatch 403 (?i)(~|`|<|>|:|;|,|%|\\|\s|\{|\}|\[|\]|\|)
	RedirectMatch 403 (?i)/(=|\$&|_mm|cgi-|etc/passwd|muieblack)
	RedirectMatch 403 (?i)(&pws=0|_vti_|\(null\)|\{\$itemURL\}|echo(.*)kae|etc/passwd|eval\(|self/environ)
	RedirectMatch 403 (?i)\.(aspx?|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rar|rdf)$
	RedirectMatch 403 (?i)/(^$|(wp-)?config|mobiquo|phpinfo|shell|sqlpatch|thumb|thumb_editor|thumbopen|timthumb|webshell)\.php
</IfModule>

# 6G:[USER AGENTS]
<IfModule mod_setenvif.c>
	SetEnvIfNoCase User-Agent ([a-z0-9]{2000}) bad_bot
	SetEnvIfNoCase User-Agent (archive.org|binlar|casper|checkpriv|choppy|clshttp|cmsworld|diavol|dotbot|extract|feedfinder|flicky|g00g1e|harvest|heritrix|httrack|kmccrew|loader|miner|nikto|nutch|planetwork|postrank|purebot|pycurl|python|seekerspider|siclab|skygrid|sqlmap|sucker|turnit|vikspider|winhttp|xxxyy|youda|zmeu|zune) bad_bot
	
	# Apache < 2.3
	<IfModule !mod_authz_core.c>
		Order Allow,Deny
		Allow from all
		Deny from env=bad_bot
	</IfModule>

	# Apache >= 2.3
	<IfModule mod_authz_core.c>
		<RequireAll>
			Require all Granted
			Require not env bad_bot
		</RequireAll>
	</IfModule>
</IfModule>

# 6G:[BAD IPS]
<Limit GET HEAD OPTIONS POST PUT>
	Order Allow,Deny
	Allow from All
	# uncomment/edit/repeat next line to block IPs
	# Deny from 123.456.789
</Limit>

Sources: https://www.linkedin.com/learning/wordpress-developing-secure-sites