2026年11月14日
2026年11月14日
Password-Protect a Directory with .htaccess (Basic Auth for WordPress)
This article is a localized edition of the Japanese original. Read the Japanese original
When you add Basic authentication in .htaccess, the browser asks for a username and password before it opens the directory you specify. It is a way to add a second layer, separate from the WordPress login, in front of a pre-launch test site, a folder of files shared only with certain people, or the WordPress admin area (/wp-admin/).
This article focuses on the Apache 2.4 syntax and covers the files you must not lock when you use it with WordPress, the Nginx equivalent, and what to check when it does not work.
Where this method works
- The web server is Apache (or LiteSpeed, which reads
.htaccess). Nginx does not read.htaccess, so you write the settings in the server configuration instead (see below). - To put authentication settings in
.htaccess, the server must allow them withAllowOverride AuthConfig(orAll). If it does not, you get a 500 error. Most shared hosting plans allow it. - You can run the
htpasswdcommand over SSH, or your hosting control panel has a feature such as "access restriction" or "Basic authentication". If the control panel offers it, that is usually easier, and the password file ends up where the hosting company recommends. - The site is served over HTTPS. Basic authentication sends the username and password without encryption, so over plain HTTP they can be read in transit.
Step 1: Create the password file (.htpasswd)
Log in to the server over SSH and create the password file with the htpasswd command that comes with Apache.
# Create a new file and add the user wpuser (-c creates the file; using it on an existing file overwrites it)
# -B stores the password in bcrypt format
htpasswd -c -B /home/username/.htpasswd wpuser
# Leave out -c when you add more users
htpasswd -B /home/username/.htpasswd another-user
# Remove a user
htpasswd -D /home/username/.htpasswd another-user
The command asks you to type the password twice. The resulting file has one line per user, with the username and the hashed password, such as wpuser:$2y$05$....
Put the password file somewhere it cannot be opened from the web (outside the document root). Replace /home/username/ in the example with your account's home directory or whatever fits your server. Apache's default configuration blocks files whose names start with .ht from being read from outside, even inside the document root, but keeping the file outside is safer when you can.
In .htaccess, AuthUserFile takes the full path of this file (a path starting with /). If you are not sure of the full path, run pwd in the directory where you put the password file.
Step 2: Put .htaccess in the directory you want to protect
Create .htaccess in the directory you want to protect (for example /members/) with the following. Authentication then applies to that directory and every file under it.
# /members/.htaccess
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /home/username/.htpasswd
Require valid-user
AuthNameis the name that identifies the protected area (the realm). Within the same realm, the browser reuses the username and password once entered. Non-ASCII text can be garbled, so use plain ASCII letters and numbers.Require valid-userlets in any user listed in the password file. To let in only specific users, write something likeRequire user wpuser.
Protecting the WordPress admin area (wp-admin)
To put Basic authentication on /wp-admin/, create .htaccess inside the wp-admin folder. (WordPress itself does not ship this file. If one already exists, check its contents before adding to it.)
# wp-admin/.htaccess
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /home/username/.htpasswd
Require valid-user
# Exclude files that the public side of the site also calls
<Files "admin-ajax.php">
Require all granted
</Files>
admin-ajax.php is also called by features that logged-out visitors use, such as contact form submissions, filtered search and WooCommerce cart updates. If you put it behind authentication, visitors get a password prompt or those features stop working. If you use a plugin that receives form submissions from logged-out visitors through admin-post.php, exclude admin-post.php the same way.
Satisfy Any, Order and Allow from all are Apache 2.2 syntax. In Apache 2.4, use Require all granted.
The official WordPress documentation (the page on brute force attacks) treats Basic authentication over all of /wp-admin/ as an older approach that tends to break Ajax-based plugins and hurt usability, and it presents login attempt limits, two-factor authentication and IP address restrictions instead. If you use Basic authentication, add the exclusions shown above and then confirm that the site's features still work.
Protecting wp-login.php
The login screen, wp-login.php, sits in the WordPress root rather than in the wp-admin folder, so wp-admin/.htaccess does not cover it. Add the following to the .htaccess in the root. Put it outside (above) the # BEGIN WordPress to # END WordPress block that WordPress rewrites.
# Root .htaccess (place above # BEGIN WordPress)
<Files "wp-login.php">
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /home/username/.htpasswd
Require valid-user
</Files>
Besides logging in, wp-login.php also handles logging out, password resets and the password form of password-protected posts. On a site that uses password-protected posts or user registration, keep in mind that visitors will also see the Basic authentication prompt.
Step 3: Check that it works
Open the protected directory in a browser and confirm that it asks for a username and password, and that the content (the login screen, for WordPress) appears after you enter them correctly. To check from the command line:
# Without credentials: should be 401
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/wp-admin/
# With credentials: should be something other than 401 (such as a 302 to the WordPress login screen)
curl -s -o /dev/null -w '%{http_code}\n' -u wpuser https://example.com/wp-admin/
# admin-ajax.php should not return 401 even without credentials (WordPress returns 400 when there are no parameters)
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/wp-admin/admin-ajax.php
With -u wpuser, curl prompts you for the password. Also test the features on the public side that use admin-ajax.php, such as contact forms and search.
On Nginx
On Nginx, you write the settings in the server configuration instead of .htaccess. You can create the password file with htpasswd, but which hash formats Nginx can read depends on the server's crypt() support. If you are unsure, create it without -B (Apache's MD5 format) or with openssl passwd -apr1.
# Example: create a password file in Apache's MD5 format
printf 'wpuser:%s\n' "$(openssl passwd -apr1)" | sudo tee /etc/nginx/.htpasswd
The configuration below protects /wp-admin/. Adjust the PHP-FPM socket path and how you include the fastcgi settings to match your setup (the example uses Ubuntu's snippets/fastcgi-php.conf).
location ^~ /wp-admin/ {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
# Exclude admin-ajax.php from authentication
location = /wp-admin/admin-ajax.php {
auth_basic off;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
# Handle PHP inside wp-admin here as well
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
The key point is to add ^~ to the location and handle PHP inside it. If you write only location /wp-admin/ { auth_basic ...; }, requests for PHP files such as /wp-admin/index.php are handled by the server-wide location ~ \.php$ instead, so no authentication applies. In a test with Nginx in Docker, the version without ^~ served /wp-admin/index.php without authentication, while the configuration above returned 401 and let only admin-ajax.php through without credentials.
After changing the configuration, check the syntax and then reload.
sudo nginx -t && sudo systemctl reload nginx
When it does not work
| Symptom | What to check |
|---|---|
| 500 Internal Server Error | A typo in .htaccess, or whether AllowOverride allows authentication settings. The server's error log shows the cause |
| The correct password is rejected | Whether the AuthUserFile path is a correct full path starting with /, whether the web server can read the password file (permissions), or a typo in the username |
| Visitors also get a password prompt | Whether admin-ajax.php (and admin-post.php if needed) is excluded, and whether a plugin loads another file inside wp-admin from the public side |
| You are locked out of the admin area | Over FTP, SFTP or a file manager, rename the .htaccess you added to something like htaccess-off to remove the authentication |
If you use a caching plugin or a CDN, the authentication prompt or a 401 response can be cached and make the behavior confusing. Use a private browser window or curl when you check.
Summary
- Create the password file with
htpasswd -c -Band keep it outside the document root - In the
.htaccessof the directory to protect, writeAuthType Basic,AuthUserFile(full path) andRequire valid-user - When protecting WordPress
wp-admin, excludeadmin-ajax.phpwithRequire all granted. Protectwp-login.phpwith<Files>in the root.htaccess - On Nginx, unless you handle PHP inside
location ^~ /wp-admin/, PHP files are not protected - Use Basic authentication on HTTPS sites, together with login attempt limits and two-factor authentication
Related articles
- How to restrict wp-admin by IP address with .htaccess
- How to add Basic authentication to the WordPress admin area
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)