
How to secure WordPress with .htaccess rules?
- Sajjad
- WordPress, Security
- 11 Sep, 2026
You can secure WordPress with .htaccess rules by adding directives to the .htaccess file in your site's root (outside the # BEGIN WordPress block) that deny access to sensitive files like wp-config.php, turn off directory browsing, block direct access to include-only core files, restrict wp-admin or wp-login.php to trusted users, and stop PHP from running in the uploads folder. These rules are enforced by Apache or LiteSpeed before WordPress even loads, which makes them fast and hard to bypass.
.htaccess is a small configuration file, but it's one of the most powerful hardening tools available on Apache-based hosting. This article explains how the file works, how to edit it safely, and which proven rules are worth adding, with copy-pasteable snippets written in modern Apache 2.4 syntax.
What Is the .htaccess File?
.htaccess (short for "hypertext access") is a per-directory configuration file read by the Apache web server and by LiteSpeed, which is compatible with Apache's rules. Any directives inside it apply to the folder it sits in and all folders beneath it.
WordPress already uses the .htaccess file in your site's root for pretty permalinks. When you save your settings under Settings > Permalinks, WordPress writes a block like this:
# 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
WordPress can rewrite anything between the # BEGIN WordPress and # END WordPress markers at any time, so never put your own rules inside that block. Add security rules above it or below it instead.
Does This Apply to Your Server?
.htaccess files only work on Apache and LiteSpeed (including OpenLiteSpeed with rewrite support enabled). If your site runs on Nginx, the file is ignored completely, and you need equivalent rules in your Nginx server configuration. Many hosts use Nginx in front of Apache as a proxy, in which case .htaccess rules still work because Apache processes the request. If you're not sure, ask your host.
How to Edit .htaccess Safely
A single typo in .htaccess can cause a 500 Internal Server Error across your whole site, so a careful process matters.
-
Back up the file: Connect with SFTP or your host's file manager and download a copy of the existing
.htaccessfrom your WordPress root before you change anything. -
Show hidden files: Files starting with a dot are hidden by default in many FTP clients and file managers. Enable the "show hidden files" option if you can't see it.
-
Edit with a plain text editor: Use a code editor, not a word processor, so no smart quotes or formatting sneak in.
-
Add one rule at a time: Save after each change and reload your site in a private browser window. If something breaks, you know exactly which rule caused it.
-
Restore if needed: If you see a 500 error, upload your backup copy immediately, then review the rule you added.
Some rules need specific Apache permissions to work in .htaccess. Require directives need AllowOverride AuthConfig (or All), and Options needs AllowOverride Options. Most shared hosts allow these, but if a rule triggers a 500 error, that's often the reason.
Protect wp-config.php
Your wp-config.php file contains your database credentials and security keys. PHP files aren't normally displayed as text, but if PHP ever stops working on the server, a misconfiguration could expose the raw file. Denying direct access removes that risk:
# Protect wp-config.php
<Files wp-config.php>
Require all denied
</Files>
WordPress itself loads wp-config.php through PHP's file system, not through a web request, so this rule doesn't affect your site.
Protect .htaccess and Other Hidden Files
Most Apache installations already block files starting with .ht, but adding the rule yourself costs nothing and also protects other hidden files like .env, .git, and .user.ini that may end up in your web root:
# Block access to hidden files and folders (dotfiles)
<FilesMatch "^\.">
Require all denied
</FilesMatch>
# Block version control folders
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule (^|/)\.(git|svn|hg)(/|$) - [F,L]
</IfModule>
If you use Let's Encrypt with HTTP validation, it needs access to /.well-known/acme-challenge/. The FilesMatch rule targets file names, and challenge files don't start with a dot, so validation continues to work. The rewrite rule only targets version control folders.
Disable Directory Browsing
If a folder has no index.php or index.html file, some servers display a list of every file in it. That can reveal which plugins you use, backup archives, or other files you'd rather keep private. Turning off directory listings takes one line:
# Disable directory browsing
Options -Indexes
After adding it, visitors who request a folder without an index file will see a 403 Forbidden error instead of a file list.
Block Access to Sensitive and Unnecessary Files
WordPress ships with a few files that reveal your version or aren't needed in production, and developers sometimes leave behind backups, logs, or database dumps. Block them in one go:
# Block sensitive file types and informational core files
<FilesMatch "(?i)(\.(bak|backup|old|orig|swp|sql|log|ini|sh|dist)|~)$|^(readme\.html|license\.txt|wp-config-sample\.php)$">
Require all denied
</FilesMatch>
This rule blocks:
- Backup and temporary files: Such as
wp-config.php.bak,wp-config.php.old, or editor swap files, which may contain credentials in plain text. - Database dumps and logs: Files ending in
.sqlor.log, includingwp-content/debug.logif debug logging was left on. - Configuration files: Such as
php.inior.user.ini. - Informational core files:
readme.html,license.txt, andwp-config-sample.php, which aren't needed for your site to run.
If a plugin needs to serve one of these file types publicly, adjust the pattern. Better still, don't store backups or dumps inside your web root at all.
Block Direct Access to Include-Only Core Files
Some files in wp-includes and wp-admin/includes are only meant to be loaded by other PHP files, never requested directly in a browser. WordPress's own hardening documentation provides this rule set to block them:
# Block the include-only files.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>
Place it outside the # BEGIN WordPress block. There's one important caveat: this rule set isn't suitable for older WordPress Multisite networks that still serve files through ms-files.php, because the wp-includes rule would block it. If you run Multisite, test carefully or skip the wp-includes lines.
Block PHP Execution in the Uploads Folder
The uploads folder should only contain media, so no PHP file there should ever run. Create a separate .htaccess file inside wp-content/uploads/ with this content:
# wp-content/uploads/.htaccess
<FilesMatch "\.(?i:php[0-9]?|phtml|phar|pht|phps)$">
Require all denied
</FilesMatch>
This one rule stops most uploaded backdoors from executing even if an attacker manages to place one on your server.
Restrict Access to wp-admin by IP Address
If you and your team always log in from fixed IP addresses, you can restrict wp-admin to those addresses. Create an .htaccess file inside the wp-admin folder (not the root):
# wp-admin/.htaccess
Require ip 203.0.113.10
Require ip 198.51.100.25
# Allow front-end features that use admin-ajax.php and admin-post.php
<Files admin-ajax.php>
Require all granted
</Files>
<Files admin-post.php>
Require all granted
</Files>
Replace the example IP addresses with your own. When several Require lines appear together like this, a visitor matching any of them is allowed in.
The admin-ajax.php and admin-post.php exceptions are important. Many themes and plugins use those files for front-end features like contact forms, filters, and carts. Blocking them would break those features for visitors.
Before relying on this rule, consider the downsides:
- Dynamic IPs: Most home connections change IP addresses periodically, which could lock you out.
- Remote teams: Every editor needs a known, stable IP.
- CDNs and proxies: If your site is behind Cloudflare or another proxy, Apache may see the proxy's IP instead of the visitor's unless the server is configured to restore the real client IP with
mod_remoteip.
If IP restriction isn't practical, password protection is a good alternative.
Password-Protect wp-login.php With HTTP Authentication
HTTP Basic Authentication adds a second username and password prompt before the WordPress login page even loads. Bots hammering wp-login.php are stopped at the server level without ever touching WordPress.
Step 1: Create a Password File
If you have SSH access, create a password file outside your web root using the htpasswd tool. On Ubuntu and Debian it comes with the apache2-utils package, and on RHEL-based systems with httpd-tools:
htpasswd -c /home/youruser/.wp-htpasswd yourname
You'll be asked to enter a password. The -c flag creates the file, so leave it off when adding more users later. Many hosting control panels, such as cPanel's "Directory Privacy" tool, can create this file for you if you don't have SSH access.
Step 2: Protect the Login Page
Add this to your root .htaccess file, adjusting the path to match your password file:
# Password-protect wp-login.php
<Files wp-login.php>
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /home/youruser/.wp-htpasswd
Require valid-user
</Files>
Always use HTTPS with Basic Authentication, because the credentials are only encoded, not encrypted, on their own. Also be aware that this extra prompt appears for any user who logs in, including customers on a WooCommerce or membership site, so it's best suited to sites where only your team logs in.
Block Author Enumeration Scans
WordPress redirects a URL like /?author=1 to that author's archive, which reveals their username in the URL. Bots use this to collect usernames for brute-force attacks. This rule blocks those numeric author lookups on the front end:
# Block author ID enumeration
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/wp-admin [NC]
RewriteCond %{QUERY_STRING} (^|&)author=[0-9]+ [NC]
RewriteRule ^ - [F,L]
</IfModule>
Note that usernames may still be visible through other means, such as the REST API users endpoint, so treat this as one small layer rather than a complete fix.
Add Basic Security Headers
.htaccess can also send HTTP security headers that tell browsers how to handle your site. Here's a minimal, low-risk set:
# Basic security headers
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
Headers like Strict-Transport-Security and Content-Security-Policy need more careful planning, because a misconfiguration can break your site or lock browsers into HTTPS.
Putting It All Together
Here's how a hardened root .htaccess file might be organised. Your own file may contain extra rules from caching or security plugins, which you should leave in place:
# ---- Security rules (custom) ----
Options -Indexes
<Files wp-config.php>
Require all denied
</Files>
<FilesMatch "^\.">
Require all denied
</FilesMatch>
<FilesMatch "(?i)(\.(bak|backup|old|orig|swp|sql|log|ini|sh|dist)|~)$|^(readme\.html|license\.txt|wp-config-sample\.php)$">
Require all denied
</FilesMatch>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
# ---- End security rules ----
# BEGIN WordPress
# (leave WordPress's own rules here untouched)
# END WordPress
Remember that the uploads rule goes in wp-content/uploads/.htaccess, and the IP restriction goes in wp-admin/.htaccess.
How to Test Your Rules
After adding rules, confirm they work and haven't broken anything:
-
Check your site normally: Browse the home page, a few posts, and log in to the dashboard.
-
Request a blocked file: Visit
https://yourdomain.com/wp-config.phpandhttps://yourdomain.com/readme.html. Both should return 403 Forbidden. -
Test from the command line: Use
curlto see the status codes directly:
curl -I https://yourdomain.com/readme.html
curl -I https://yourdomain.com/wp-includes/
- Test forms and dynamic features: Submit a contact form, add something to the cart, or use any AJAX-powered feature to make sure nothing depends on a file you blocked.
Avoid Common .htaccess Mistakes
A few habits prevent most problems:
- Don't use old Apache 2.2 syntax: Directives like
Order allow,denyandDeny from allare deprecated. On Apache 2.4 they only work through a compatibility module, and mixing them withRequirecan produce confusing results. - Don't paste giant rule lists blindly: Large "firewall" rule sets copied from forums can block legitimate traffic. If you want a curated set, well-maintained options like Jeff Starr's nG firewall rules are documented and tested, but still apply them carefully.
- Don't duplicate plugin rules: Security and caching plugins often write their own
.htaccesssections. Check what's already there before adding the same rule twice. - Don't edit inside the WordPress markers: Your changes will be overwritten the next time permalinks are saved.
FAQ: Securing WordPress With .htaccess
It's in the root folder of your WordPress installation, the same folder that contains wp-config.php and wp-content. It's a hidden file, so you may need to enable the option to show hidden files in your FTP client or file manager.
Upload the backup copy you made before editing, or rename the broken file via SFTP to disable it. Then add your rules back one at a time to find the one causing the problem.
No. Nginx ignores .htaccess files entirely. You need to add equivalent rules to your Nginx server configuration or ask your host to do it for you.
You shouldn't. WordPress may rewrite that block whenever permalinks are saved or certain plugins run, which would remove your rules. Place custom rules above or below it.
Use Require all denied. It is the correct syntax for Apache 2.4 and later, while Order and Deny belong to the deprecated Apache 2.2 access control model.
Often, yes. .htaccess rules are great for blocking files and paths, but they don't scan for malware, monitor file changes, or manage login security. The two approaches complement each other.
No. Apache processes simple rules like these very quickly, and blocking unwanted requests early can actually reduce server load.
Conclusion
The .htaccess file gives you a fast, server-level way to harden WordPress without adding another plugin. Protecting wp-config.php, blocking hidden and sensitive files, turning off directory browsing, restricting include-only core files, and stopping PHP from running in the uploads folder all take just a few lines, and each one closes a door that attackers commonly try.
Work carefully: back up the file first, add one rule at a time, keep your custom rules outside the WordPress markers, and test after every change. If your site runs on Nginx, translate the same ideas into your server configuration instead. Done right, a well-organised .htaccess file becomes a quiet, reliable layer of protection that works before WordPress even starts.


