The .htaccess file is the most powerful configuration file available on Apache-based WordPress hosting. Every request to your site passes through it before WordPress even loads. A properly configured .htaccess can block brute force attacks, enable browser caching, compress responses, prevent hotlinking, and redirect traffic, all at the server level, before PHP executes a single line of code.
This guide covers practical .htaccess tweaks for WordPress security and performance. Each snippet is production-tested and explained so you understand what it does before adding it to your site. If you are unfamiliar with WordPress server-level configuration, start with our WordPress debug log guide to understand how WordPress interacts with its environment.
Important: Always back up your existing .htaccess file before making changes. A syntax error can take your entire site offline. Test each change individually so you can identify which rule causes problems if something breaks.
Where Is the .htaccess File?
The WordPress .htaccess file lives in your site’s root directory (same level as wp-config.php). By default, WordPress generates a minimal .htaccess for permalink rewrites:
Never edit between the # BEGIN WordPress and # END WordPress comments. WordPress regenerates this block when you update permalink settings. Add all custom rules above the # BEGIN WordPress block.
If you cannot see .htaccess in your file manager, enable “show hidden files”, the dot prefix makes it hidden by default on Unix systems.
Security Tweaks
1. Block XML-RPC (Prevent Brute Force and DDoS)
xmlrpc.php is a legacy remote publishing interface that most WordPress sites no longer need. It is one of the most targeted files for brute force attacks and DDoS amplification. Unless you use the WordPress mobile app, Jetpack, or a tool that specifically requires XML-RPC, block it:
2. Protect wp-config.php
wp-config.php contains your database credentials, authentication keys, and salts. It should never be accessible via HTTP:
3. Disable Directory Browsing
By default, Apache may list directory contents when no index.php exists. This exposes your file structure to anyone who navigates to /wp-content/uploads/ or /wp-includes/:
4. Block PHP Execution in Uploads
The wp-content/uploads/ directory should only contain media files. If an attacker uploads a PHP shell disguised as an image, this rule prevents it from executing:
Create a separate .htaccess file inside wp-content/uploads/ with just this rule. This is one of the most effective defenses against webshell attacks, even if a malicious file gets uploaded, it cannot execute.
5. Protect .htaccess Itself
6. Block Suspicious User Agents
Block common attack tools and bad bots that scan for vulnerabilities:
This blocks known vulnerability scanners by name. Legitimate users will never have these user agent strings.
7. Restrict wp-admin Access by IP
If you have a static IP address, you can restrict wp-admin access to only your IP. Create a separate .htaccess inside the wp-admin/ directory:
Warning: This locks everyone else out of wp-admin, including other team members. Only use this on single-admin sites with a static IP. If your IP changes, you will need FTP/SSH access to update the file.
8. Block Author Enumeration
Attackers scan /?author=1, /?author=2, etc. to discover usernames for brute force attacks:
9. Prevent Image Hotlinking
Hotlinking occurs when other sites embed your images directly, consuming your bandwidth without serving your content:
Replace yourdomain.com with your actual domain. The Google and Bing exceptions ensure your images still appear in search results.
Performance Tweaks
10. Enable Gzip/Deflate Compression
Compression reduces the size of HTML, CSS, JavaScript, and XML responses by 60-80%, dramatically reducing transfer times:
Images (JPEG, PNG, WebP) are already compressed, so gzip is skipped for those. Fonts (WOFF2 in particular) are pre-compressed but including WOFF/TTF covers older font formats.
11. Set Browser Caching Headers (Expires)
Browser caching tells visitors’ browsers to store static files locally instead of re-downloading them on every page load. This is one of the highest-impact performance optimizations:
Static assets (CSS, JS, images, fonts) get a 1-year cache because WordPress and most plugins append version query strings (?ver=6.9) that bust the cache on updates. HTML and data formats are set to zero (always re-validated) because content changes frequently.
12. Add Cache-Control Headers
For more granular control, use Cache-Control headers alongside or instead of Expires:
The immutable directive tells browsers that the file will never change at that URL, eliminating revalidation requests entirely. This works because WordPress uses versioned URLs for assets.
13. Enable Keep-Alive Connections
Keep-Alive allows multiple HTTP requests to reuse the same TCP connection, reducing latency for pages that load multiple resources:
14. Disable ETags
ETags can cause caching inconsistencies on load-balanced servers. When using Expires and Cache-Control headers, ETags are redundant:
Redirect Rules
15. Force HTTPS
Redirect all HTTP traffic to HTTPS. This should be the first rule in your .htaccess after any server-level directives:
16. Force www or Non-www
Choose one canonical version to avoid duplicate content issues:
17. Custom 301 Redirects
Redirect old URLs to new locations after restructuring content or migrating from another platform:
For bulk redirects (migration scenarios), consider using WP-CLI search-replace for database URL updates instead of adding hundreds of redirect rules to .htaccess, which slows down every request.
Complete Production .htaccess Template
Here is a combined template with the most impactful rules. Copy, customize (replace yourdomain.com), and add above the # BEGIN WordPress block:
.htaccess vs Nginx: What If You Are Not on Apache?
.htaccess only works on Apache web servers. If your host uses Nginx (Kinsta, Flywheel, SpinupWP, many VPS setups), you need equivalent Nginx directives in your server configuration file instead.
| .htaccess (Apache) | Nginx Equivalent |
|---|---|
Options -Indexes | autoindex off; |
mod_deflate | gzip on; |
mod_expires | expires directive in location block |
<Files xmlrpc.php> | location = /xmlrpc.php { deny all; } |
RewriteRule | rewrite or return 301 |
Most managed WordPress hosts that use Nginx handle security and caching at the server level. Check with your host before duplicating these rules.
Performance Impact
The combined effect of these optimizations on a typical WordPress site:
| Optimization | Impact | Effort |
|---|---|---|
| Gzip compression | 60-80% reduction in transfer size | One-time config |
| Browser caching (Expires) | Eliminates repeat downloads for returning visitors | One-time config |
| Cache-Control immutable | Eliminates revalidation requests for static assets | One-time config |
| Keep-Alive | Reduces TCP handshake overhead for multi-resource pages | One-time config |
| ETag removal | Prevents unnecessary revalidation on load-balanced setups | One-time config |
| Block XML-RPC | Eliminates brute force vector, reduces server load | One-time config |
For deeper WordPress performance tuning beyond .htaccess, including database optimization and object caching, see our guides on fixing wp_options autoload bloat and cleaning up WordPress transients.
Troubleshooting
Site returns 500 Internal Server Error after editing .htaccess
A syntax error in .htaccess causes a 500 error. Connect via FTP or SSH, rename .htaccess to .htaccess.bak, and your site will come back online (with broken permalinks). Then restore your backup and identify the problematic rule by adding rules one at a time.
Redirect loops after adding HTTPS redirect
If your host already handles HTTPS redirection at the server level or CDN level (Cloudflare), adding an .htaccess HTTPS redirect can create an infinite loop. Check if your host or CDN already redirects before adding the rule. For Cloudflare, use the “Always Use HTTPS” page rule instead.
Caching headers not applied
The mod_expires and mod_headers Apache modules must be enabled. On shared hosting, these are usually available. On VPS/dedicated, enable them with sudo a2enmod expires headers and restart Apache.
Rules not taking effect
Apache must have AllowOverride All set in its virtual host configuration for .htaccess to be processed. If AllowOverride None is set, Apache ignores .htaccess entirely. Contact your hosting provider to verify this setting.
Frequently Asked Questions
Is .htaccess still relevant with modern WordPress hosting?
On Apache-based hosting (most shared hosting, many VPS setups), yes. On Nginx-based managed hosting (Kinsta, Flywheel), .htaccess is not used, the host handles equivalent configurations at the server level. Check your hosting stack before investing time in .htaccess tweaks.
Can .htaccess changes break my site?
Yes. A syntax error causes a 500 Internal Server Error site-wide. Always back up the file before editing, make one change at a time, and test immediately. If your site goes down, rename or remove .htaccess via FTP/SSH to restore access.
Should I use a security plugin instead of .htaccess rules?
Both serve different purposes. .htaccess rules execute at the Apache level before PHP loads, making them faster and more resource-efficient. Security plugins (Wordfence, iThemes Security) provide broader protections (firewall, malware scanning, login limiting) that require PHP. The best approach is using both: .htaccess for server-level blocks and a security plugin for application-level protection.
Do I need all of these rules?
No. Start with the highest-impact rules: Force HTTPS, block XML-RPC, enable gzip compression, and set browser caching. Add additional security rules based on your specific threat model. The complete production template above includes the most universally beneficial rules.
How do .htaccess performance tweaks compare to a caching plugin?
They are complementary, not competing. .htaccess handles browser-side caching (telling browsers to store files locally) and compression. Caching plugins (WP Super Cache, W3 Total Cache, LiteSpeed Cache) handle server-side caching (generating static HTML from dynamic PHP). Use both for maximum WordPress performance.
Last modified: February 24, 2026