Sometimes you need to restrict access to a page, a directory, or an entire site to only those who know the password. Whether it is a staging environment that clients should not stumble upon, an admin panel that needs an extra layer of defense, or a private section for internal team members, Apache's built-in HTTP authentication provides a straightforward solution without writing any application code.
This guide covers everything you need to set up password protection using .htaccess: creating and managing password files, configuring authentication directives, choosing between user-level and group-level access, and understanding the security limitations you should be aware of before relying on this mechanism in production.
htpasswd Files
The foundation of Apache's password protection is the htpasswd file, a plain text file that stores usernames and their hashed passwords. Apache reads this file every time someone attempts to access a protected resource and compares the credentials they provide against the entries in the file.
Creating an htpasswd File
The htpasswd command-line utility (included with every Apache installation) creates and manages password files. Here is how to use it.
Create a new file with the first user:
htpasswd -c /etc/apache2/.htpasswd admin- The
-cflag creates a new file. If the file already exists, it is overwritten completely. /etc/apache2/.htpasswdis the path where the file will be stored.adminis the username.
After running this command, you are prompted to enter and confirm a password:
New password: ********
Re-type new password: ********
Adding password for user adminAdd additional users to an existing file:
htpasswd /etc/apache2/.htpasswd editor
htpasswd /etc/apache2/.htpasswd johnNotice there is no -c flag when adding to an existing file. This is critical.
Update an existing user's password:
htpasswd /etc/apache2/.htpasswd adminIf the username already exists in the file, htpasswd replaces the old password with the new one.
Delete a user:
htpasswd -D /etc/apache2/.htpasswd johnThe -D flag removes the specified user from the file.
View the contents of the file:
cat /etc/apache2/.htpasswdOutput:
admin:$apr1$xyz12345$A1B2C3D4E5F6G7H8I9J0K1
editor:$apr1$abc67890$L2M3N4O5P6Q7R8S9T0U1V2Each line contains a username and a hashed password separated by a colon. The password is never stored in plain text.
Storing It Outside the Web Root
The htpasswd file contains password hashes that, while not plain text, should never be accessible via a browser. If someone can download your htpasswd file, they can run offline brute-force attacks against the hashes at their leisure.
Wrong approach:
/var/www/html/ ← Document root (publicly accessible)
├── index.html
├── .htaccess
├── .htpasswd ← DANGER: Inside the web root!
└── admin/
└── index.phpIf the file is inside your document root, a direct request to https://example.com/.htpasswd could potentially serve its contents. While Apache blocks .ht* files by default, misconfigurations, alternative web servers, or proxy setups could expose it.
Correct approach:
/etc/apache2/.htpasswd ← Outside the web root (not accessible via HTTP)
/var/www/html/ ← Document root
├── index.html
├── .htaccess
└── admin/
└── index.phpStore the htpasswd file in a location that is completely outside the document root. Common locations include:
/etc/apache2/.htpasswd/home/username/.htpasswd(on shared hosting)/var/www/.htpasswd(one level above the typical document root)
Your .htaccess file references the htpasswd file using its absolute filesystem path, so it works regardless of where the file is located.
Password Hashing
The htpasswd utility supports several hashing algorithms. The algorithm determines how securely the passwords are stored and how resistant they are to brute-force attacks.
Default behavior (recommended):
htpasswd /etc/apache2/.htpasswd usernameExplicitly specify bcrypt (most secure):
htpasswd -B /etc/apache2/.htpasswd usernameThe -B flag forces bcrypt hashing, which is the strongest option available.
Specify bcrypt cost factor:
htpasswd -B -C 12 /etc/apache2/.htpasswd usernameThe -C flag sets the bcrypt cost factor (default is 5, range is 4-17). Higher values are slower to compute, making brute-force attacks more expensive. A value of 10-12 is a good balance between security and login speed.
Here is a comparison of the available algorithms:
| Flag | Algorithm | Hash Example Prefix | Security Level | Notes |
|---|---|---|---|---|
-B |
bcrypt | $2y$ |
Strongest | Recommended. Slow by design. |
| none | APR1 (MD5) | $apr1$ |
Moderate | Default on many systems. Adequate. |
-d |
crypt | No prefix | Weak | Legacy Unix crypt. Only 8-char passwords. |
-s |
SHA-1 | {SHA} |
Weak | No salt. Vulnerable to rainbow tables. |
-p |
Plain text | No prefix | None | Never use. Passwords stored in clear. |
Wrong approach:
htpasswd -p /etc/apache2/.htpasswd username
htpasswd -s /etc/apache2/.htpasswd usernameCorrect approach:
htpasswd -B /etc/apache2/.htpasswd usernameAuthType Basic
With the htpasswd file created, the next step is configuring .htaccess to require authentication. The AuthType directive specifies which authentication scheme Apache should use.
AuthType BasicBasic authentication works by having the browser prompt the user for a username and password. The browser then sends these credentials with every subsequent request to the protected area, encoded in Base64 in the Authorization header.
Here is a minimal, complete configuration:
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /etc/apache2/.htpasswd
Require valid-userWhen a user visits a page protected by this configuration, their browser displays a login dialog. After entering valid credentials, they gain access. The browser remembers the credentials for the session (until the browser is closed or the cache is cleared).
How Basic Auth Works Under the Hood
The exchange between browser and server follows this sequence:
- Client requests the protected resource.
- Server responds with
401 Unauthorizedand aWWW-Authenticate: Basic realm="Restricted Area"header. - Browser shows a login dialog to the user.
- User enters username and password.
- Browser sends the request again with an
Authorization: Basic dXNlcjpwYXNzheader (Base64-encodedusername:password). - Server decodes the header, looks up the username in the htpasswd file, hashes the provided password, and compares it to the stored hash.
- If they match, the server grants access and serves the resource. If not, step 2 repeats.
AuthName and AuthUserFile
These two directives work alongside AuthType to complete the authentication configuration.
AuthName
AuthName sets the realm displayed in the browser's login dialog. It tells the user what they are logging into.
AuthName "Restricted Area"The browser typically displays this as:
The server https://example.com requires a username and password.
The server says: Restricted Area
Username: [________]
Password: [________]
[Cancel] [Log In]The realm name also has a functional purpose: browsers group credentials by realm. If two different directories use the same AuthName, the browser automatically sends the same credentials to both without prompting again. If they use different realm names, the browser treats them as separate authentication scopes.
AuthName "Admin Panel"AuthName "Reports Section"With different realm names, users must authenticate separately for each section, even if their credentials are valid for both.
AuthUserFile
AuthUserFile specifies the absolute filesystem path to the htpasswd file containing the usernames and password hashes.
AuthUserFile /etc/apache2/.htpasswdThis must be an absolute path, not a relative one. Apache needs to know exactly where on the filesystem to find the file.
Wrong approach:
AuthUserFile .htpasswd
AuthUserFile ../secrets/.htpasswdCorrect approach:
AuthUserFile /etc/apache2/.htpasswd
AuthUserFile /home/username/.htpasswdIf you are on shared hosting and unsure of the absolute path, you can find it by creating a PHP file that outputs the server's document root:
<?php
echo $_SERVER['DOCUMENT_ROOT'];
// Output example: /home/username/public_htmlIf the document root is /home/username/public_html, you could store your htpasswd file at /home/username/.htpasswd (one level above the web root).
Complete Configuration Examples
Protect an entire directory:
AuthType Basic
AuthName "Admin Panel"
AuthUserFile /etc/apache2/.htpasswd
Require valid-userProtect a single file:
<Files "secret-report.pdf">
AuthType Basic
AuthName "Confidential Document"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
</Files>Protect specific file types:
<FilesMatch "\.(pdf|xlsx|docx)$">
AuthType Basic
AuthName "Document Library"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
</FilesMatch>Require valid-user vs Require user
The Require directive determines which authenticated users are granted access. There are two main approaches.
Require valid-user
Require valid-userThis grants access to any user listed in the htpasswd file, as long as they provide the correct password. If the htpasswd file contains 50 users, all 50 can access the protected resource.
This is the most common choice when everyone in the password file should have the same level of access.
Require user
Require user admin editorThis grants access only to the specifically named users, even if other valid users exist in the htpasswd file. A user named john could have a valid entry in the htpasswd file, but if he is not listed in the Require user directive, he is denied access with a 401 response.
This is useful when different directories should be accessible to different people:
AuthType Basic
AuthName "Admin Panel"
AuthUserFile /etc/apache2/.htpasswd
Require user admin superadminAuthType Basic
AuthName "Reports"
AuthUserFile /etc/apache2/.htpasswd
Require user admin editor analystIn this setup, the admin user can access both areas, editor and analyst can only access reports, and superadmin can only access the admin panel.
Combining Require Directives
You can combine multiple Require directives with authorization containers for more complex rules:
Allow specific users OR specific IPs:
AuthType Basic
AuthName "Mixed Access"
AuthUserFile /etc/apache2/.htpasswd
<RequireAny>
Require ip 203.0.113.0/24
Require user admin editor
</RequireAny>Clients from the office network get in without a login prompt. Everyone else must authenticate as either admin or editor.
Require authentication AND a specific IP:
AuthType Basic
AuthName "High Security"
AuthUserFile /etc/apache2/.htpasswd
<RequireAll>
Require user admin
Require ip 203.0.113.0/24
</RequireAll>The user must be admin AND must be connecting from the specified IP range. Both conditions must be true.
Common Mistake: Forgetting AuthType or AuthUserFile
Wrong approach:
AuthName "Protected"
Require valid-userApache will return a 500 Internal Server Error because it does not know what authentication method to use or where to find the password file. All four directives (AuthType, AuthName, AuthUserFile, Require) must be present together.
Correct approach:
AuthType Basic
AuthName "Protected"
AuthUserFile /etc/apache2/.htpasswd
Require valid-userLimitations of Basic Auth
HTTP Basic authentication is simple and effective for many scenarios, but it has significant limitations you should understand before relying on it.
No Encryption Without HTTPS
Basic authentication transmits credentials encoded in Base64, which is trivially reversible. Without HTTPS, anyone on the same network (coffee shop Wi-Fi, corporate network, ISP) can intercept and read the username and password.
Authorization: Basic YWRtaW46cGFzc3dvcmQxMjM=
admin:password123Always use Basic auth over HTTPS. If your site does not have HTTPS configured, set it up before enabling password protection. A redirect to HTTPS should come before any authentication:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
AuthType Basic
AuthName "Secure Area"
AuthUserFile /etc/apache2/.htpasswd
Require valid-userNo Logout Mechanism
Basic authentication has no built-in logout. Once a browser has accepted credentials, it continues sending them with every request until the browser is closed (and sometimes even after, due to credential caching). There is no "logout" button, no session expiration, and no way for the server to tell the browser to forget the credentials.
This makes Basic auth unsuitable for applications where users need to log out, switch accounts, or have their sessions expire after a period of inactivity.
No Brute-Force Protection
Apache's Basic auth has no built-in rate limiting or account lockout mechanism. An attacker can try thousands of username and password combinations per second without being blocked. To mitigate this, consider:
- Using strong, long passwords.
- Combining Basic auth with IP restrictions.
- Using
fail2banor similar tools at the server level to block repeated failed attempts.
AuthType Basic
AuthName "Double Protection"
AuthUserFile /etc/apache2/.htpasswd
<RequireAll>
Require valid-user
<RequireAny>
Require ip 203.0.113.0/24
Require ip 198.51.100.0/24
</RequireAny>
</RequireAll>No Custom Login Page
The login prompt is a browser-native dialog box. You cannot style it, add your logo, include a "forgot password" link, or customize it in any way. It looks different in every browser and provides a utilitarian, often confusing experience for non-technical users.
If you need a branded login experience, you should implement form-based authentication in your application instead of relying on Basic auth.
Flat File Scalability
The htpasswd file is read and parsed on every authenticated request. With a handful of users, this is negligible. With thousands of users, it becomes a performance problem. Basic auth with htpasswd files is designed for small-scale use (dozens of users at most).
For larger user bases, consider:
mod_authn_dbdfor database-backed authentication.- Application-level authentication with proper session management.
- An identity provider with SSO (Single Sign-On).
When Basic Auth Is Appropriate
Despite its limitations, Basic auth is a perfectly good choice for:
| Scenario | Why It Works |
|---|---|
| Staging/development site protection | Quick to set up, keeps casual visitors out |
| Admin panel extra layer | Defense in depth on top of application auth |
| Small team internal tools | Simple user management, no infrastructure needed |
| Temporary access during maintenance | Easy to add and remove |
| CI/CD webhook protection | Machine-to-machine auth where UX does not matter |
When to Use Something Else
| Scenario | Better Alternative |
|---|---|
| Public-facing user login | Application-level form authentication |
| Hundreds or thousands of users | Database-backed auth or identity provider |
| Need for session management or logout | Application sessions with cookies |
| Compliance requirements (HIPAA, PCI, SOC2) | Enterprise identity solutions with MFA |
| Social login (Google, GitHub, etc.) | OAuth2/OpenID Connect |
Basic authentication is a tool, not a solution. It excels at quickly locking down resources during development and adding a lightweight extra barrier in front of sensitive areas. But for user-facing authentication where security, usability, and scalability matter, invest in a proper authentication system at the application level. The key is knowing when each approach is appropriate and never treating Basic auth as a substitute for real application security.