Skip to content

Instantly share code, notes, and snippets.

@vapvarun
Created February 23, 2026 19:15
Show Gist options
  • Select an option

  • Save vapvarun/06e2baeff359a1d3a2ee785cb6173bf0 to your computer and use it in GitHub Desktop.

Select an option

Save vapvarun/06e2baeff359a1d3a2ee785cb6173bf0 to your computer and use it in GitHub Desktop.
WordPress .htaccess Tweaks for Security and Performance (tweakswp.com)
# 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>
# END WordPress
# Block XML-RPC
<Files xmlrpc.php>
Order Deny,Allow
Deny from all
</Files>
# Block XML-RPC except specific IPs
<Files xmlrpc.php>
Order Deny,Allow
Deny from all
Allow from 122.248.245.244
Allow from 54.217.201.243
</Files>
# Protect wp-config.php
<Files wp-config.php>
Order Allow,Deny
Deny from all
</Files>
# Disable directory browsing
Options -Indexes
# Block PHP execution in uploads
# Add this to wp-content/uploads/.htaccess
<Files "*.php">
Order Deny,Allow
Deny from all
</Files>
# Protect .htaccess from direct access
<Files .htaccess>
Order Allow,Deny
Deny from all
</Files>
# Block malicious user agents
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (havij|sqlmap|nikto|wpscan|dirbuster|nessus|acunetix) [NC]
RewriteRule .* - [F,L]
</IfModule>
# Add this to wp-admin/.htaccess
# Replace with your actual IP address
Order Deny,Allow
Deny from all
Allow from 203.0.113.50
Allow from 198.51.100.25
# Block author enumeration
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{QUERY_STRING} ^author=([0-9]+) [NC]
RewriteRule .* - [F,L]
</IfModule>
# Prevent image hotlinking
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$ [NC]
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?yourdomain\.com [NC]
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?google\. [NC]
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?bing\. [NC]
RewriteRule \.(jpg|jpeg|png|gif|webp|svg)$ - [F,NC,L]
</IfModule>
# Enable gzip compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/atom+xml
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/woff
AddOutputFilterByType DEFLATE font/woff2
# Exclude already-compressed files
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|webp|ico)$ no-gzip
</IfModule>
# Browser caching via Expires headers
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 month"
# HTML - short cache (content changes)
ExpiresByType text/html "access plus 0 seconds"
# CSS and JavaScript
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType text/javascript "access plus 1 year"
# Images
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType image/x-icon "access plus 1 year"
# Fonts
ExpiresByType font/ttf "access plus 1 year"
ExpiresByType font/otf "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType application/font-woff "access plus 1 year"
ExpiresByType application/font-woff2 "access plus 1 year"
# Data formats
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType text/xml "access plus 0 seconds"
</IfModule>
# Cache-Control headers
<IfModule mod_headers.c>
# Static assets - immutable cache
<FilesMatch "\.(css|js|ico|jpg|jpeg|png|gif|webp|svg|woff|woff2|ttf|otf)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
# HTML - no cache (always revalidate)
<FilesMatch "\.(html|htm)$">
Header set Cache-Control "no-cache, must-revalidate"
</FilesMatch>
# PHP responses - no store
<FilesMatch "\.php$">
Header set Cache-Control "no-store, no-cache, must-revalidate"
</FilesMatch>
</IfModule>
# Enable Keep-Alive
<IfModule mod_headers.c>
Header set Connection keep-alive
</IfModule>
# Disable ETags
<IfModule mod_headers.c>
Header unset ETag
</IfModule>
FileETag None
# Force HTTPS
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>
# Force non-www (remove www)
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
</IfModule>
# OR Force www (add www)
# <IfModule mod_rewrite.c>
# RewriteEngine On
# RewriteCond %{HTTP_HOST} !^www\. [NC]
# RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
# </IfModule>
# Single page redirect
Redirect 301 /old-post-slug/ https://yourdomain.com/new-post-slug/
# Redirect entire directory
RedirectMatch 301 ^/old-category/(.*)$ https://yourdomain.com/new-category/$1
# Redirect with query string matching
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{QUERY_STRING} ^p=123$ [NC]
RewriteRule ^$ /actual-post-slug/? [R=301,L]
</IfModule>
# === SECURITY ===
# Block XML-RPC
<Files xmlrpc.php>
Order Deny,Allow
Deny from all
</Files>
# Protect wp-config.php
<Files wp-config.php>
Order Allow,Deny
Deny from all
</Files>
# Protect .htaccess
<Files .htaccess>
Order Allow,Deny
Deny from all
</Files>
# Disable directory browsing
Options -Indexes
# Block author enumeration
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{QUERY_STRING} ^author=([0-9]+) [NC]
RewriteRule .* - [F,L]
</IfModule>
# === PERFORMANCE ===
# Force HTTPS
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>
# Enable gzip compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css text/javascript
AddOutputFilterByType DEFLATE application/javascript application/json
AddOutputFilterByType DEFLATE application/xml text/xml text/plain
AddOutputFilterByType DEFLATE image/svg+xml font/woff font/woff2
</IfModule>
# Browser caching
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 month"
ExpiresByType text/html "access plus 0 seconds"
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
# Cache-Control with immutable
<IfModule mod_headers.c>
<FilesMatch "\.(css|js|ico|jpg|jpeg|png|gif|webp|svg|woff|woff2)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
Header set Connection keep-alive
Header unset ETag
</IfModule>
FileETag None
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment