WordPress .htaccess Security Hardening on Apache
WordPress .htaccess Security Hardening on Apache
Use Apache .htaccess rules to add basic security hardening to a WordPress website while keeping the normal WordPress rewrite rules.
1. What this configuration does
- Blocks access to
.gitand other hidden files except.well-known. - Adds security-related HTTP response headers.
- Protects sensitive files such as
wp-config.php,.envand Composer/package files. - Disables directory listing.
- Blocks common backup and temporary file extensions.
- Blocks PHP-family files from executing inside
wp-content/uploads. - Blocks XML-RPC.
- Blocks direct PHP access inside
wp-includes. - Blocks author and REST API user enumeration rules shown in the configuration.
- Restricts HTTP methods to GET, POST, HEAD and OPTIONS.
- Preserves the standard WordPress rewrite section.
2. Back up the existing .htaccess
cd /var/www/html/your-wordpress-site
sudo cp .htaccess .htaccess.bakReplace /var/www/html/your-wordpress-site with your actual WordPress document root.
3. Edit .htaccess
sudo nano /var/www/html/your-wordpress-site/.htaccessAdd the configuration below to the WordPress root .htaccess file.
# =========================================================
# WORDPRESS SECURITY HARDENING
# =========================================================
<IfModule mod_rewrite.c>
RewriteEngine On
# -----------------------------------------------------
# Block .git and other hidden files
# -----------------------------------------------------
RewriteRule (^|/)\.git(/|$) - [F,L,NC]
RewriteRule (^|/)\.(?!well-known/) - [F,L,NC]
# -----------------------------------------------------
# Block WordPress REST API user enumeration
# -----------------------------------------------------
RewriteCond %{REQUEST_URI} ^/wp-json/wp/v2/users(?:/.*)?/?$ [NC]
RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_ [NC]
RewriteRule ^ - [F,L,NC]
</IfModule>
# =========================================================
# WordPress Security Hardening
# =========================================================
<IfModule mod_headers.c>
# -----------------------------------------------------
# Security Headers
# -----------------------------------------------------
# Prevent MIME-type sniffing
Header always set X-Content-Type-Options "nosniff"
# Prevent clickjacking
Header always set X-Frame-Options "SAMEORIGIN"
# Basic XSS protection for older browsers
Header always set X-XSS-Protection "1; mode=block"
# Referrer privacy
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# Disable unnecessary browser features
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()"
# Hide server technology information
Header unset X-Powered-By
Header always unset X-Powered-By
</IfModule>
# =========================================================
# Protect Sensitive Files
# =========================================================
<FilesMatch "^(\.env|\.git|\.gitignore|\.htaccess|\.htpasswd|wp-config\.php|composer\.(json|lock)|package(-lock)?\.json)$">
Require all denied
</FilesMatch>
# =========================================================
# Disable Directory Listing
# =========================================================
Options -Indexes
# =========================================================
# Block WordPress Configuration Backup / Temporary Files
# =========================================================
<FilesMatch "\.(bak|backup|old|orig|save|swp|tmp|sql|sql\.gz|tar|tar\.gz|zip)$">
Require all denied
</FilesMatch>
# =========================================================
# Block PHP Execution in Uploads
# IMPORTANT
# =========================================================
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^wp-content/uploads/.*\.(?:php[0-9]?|phtml|phar)$ - [F,L,NC]
</IfModule>
# =========================================================
# Block XML-RPC
# =========================================================
<Files "xmlrpc.php">
Require all denied
</Files>
# =========================================================
# Block WordPress Includes Directory
# =========================================================
<IfModule mod_rewrite.c>
RewriteRule ^wp-includes/.*\.php$ - [F,L,NC]
</IfModule>
# =========================================================
# Protect wp-config.php
# =========================================================
<Files "wp-config.php">
Require all denied
</Files>
# =========================================================
# Disable Author Enumeration
# =========================================================
<IfModule mod_rewrite.c>
RewriteCond %{QUERY_STRING} (^|&)author=\d+(&|$) [NC]
RewriteRule ^ - [F,L]
</IfModule>
# =========================================================
# Block Suspicious HTTP Methods
# =========================================================
<LimitExcept GET POST HEAD OPTIONS>
Require all denied
</LimitExcept>
# =========================================================
# BEGIN WordPress
# =========================================================
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# =========================================================
# BLOCK GIT REPOSITORY
# =========================================================
<IfModule mod_rewrite.c>
RewriteRule (^|/)\.git(/|$) - [F,L,NC]
</IfModule>
# =========================================================
# BLOCK HIDDEN FILES
# =========================================================
<IfModule mod_rewrite.c>
RewriteRule (^|/)\.(?!well-known/) - [F,L,NC]
</IfModule>
# =========================================================
# BLOCK WORDPRESS USER ENUMERATION VIA REST API
# =========================================================
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_URI} ^/wp-json/wp/v2/users/?$ [NC]
RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_ [NC]
RewriteRule ^ - [F,L]
</IfModule>
# =========================================================
# END WordPress
# =========================================================4. Make sure Apache modules are available
sudo a2enmod rewrite
sudo a2enmod headersThen reload Apache after testing the configuration.
5. Validate Apache configuration
sudo apache2ctl configtestExpected result:
Syntax OK6. Reload Apache
sudo systemctl reload apache27. Test the website
After applying the rules, test the public WordPress website, WordPress admin, login, media uploads and any plugins that depend on REST API access.
Check response headers
curl -I https://example.com/Check hidden files
A request to a protected hidden file should be denied. For example:
curl -I https://example.com/.git/configCheck the REST users endpoint
curl -I https://example.com/wp-json/wp/v2/users8. Important notes
sudo apache2ctl configtest before reloading Apache.The supplied configuration contains repeated protection rules for Git repositories, hidden files and REST API user enumeration. They are preserved here in the same organization as the source configuration; you can consolidate duplicates later after validating the live behavior.
The XML-RPC rule intentionally blocks xmlrpc.php. Any integration that requires XML-RPC should be tested before keeping that rule enabled.
Discussion (0)