Skip to content

Password Protection with .htaccess

Learn how to implement secure password protection for directories, files, or entire sites using Apache's .htaccess and htpasswd for basic authentication.

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 -c flag creates a new file. If the file already exists, it is overwritten completely.
  • /etc/apache2/.htpasswd is the path where the file will be stored.
  • admin is 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 admin

Add additional users to an existing file:

htpasswd /etc/apache2/.htpasswd editor
htpasswd /etc/apache2/.htpasswd john

Notice there is no -c flag when adding to an existing file. This is critical.

Update an existing user's password:

htpasswd /etc/apache2/.htpasswd admin

If the username already exists in the file, htpasswd replaces the old password with the new one.

Delete a user:

htpasswd -D /etc/apache2/.htpasswd john

The -D flag removes the specified user from the file.

View the contents of the file:

cat /etc/apache2/.htpasswd

Output:

admin:$apr1$xyz12345$A1B2C3D4E5F6G7H8I9J0K1
editor:$apr1$abc67890$L2M3N4O5P6Q7R8S9T0U1V2

Each 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.php

If 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.php

Store 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 username

Explicitly specify bcrypt (most secure):

htpasswd -B /etc/apache2/.htpasswd username

The -B flag forces bcrypt hashing, which is the strongest option available.

Specify bcrypt cost factor:

htpasswd -B -C 12 /etc/apache2/.htpasswd username

The -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 username

Correct approach:

htpasswd -B /etc/apache2/.htpasswd username

AuthType 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 Basic

Basic 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-user

When 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:

  1. Client requests the protected resource.
  2. Server responds with 401 Unauthorized and a WWW-Authenticate: Basic realm="Restricted Area" header.
  3. Browser shows a login dialog to the user.
  4. User enters username and password.
  5. Browser sends the request again with an Authorization: Basic dXNlcjpwYXNz header (Base64-encoded username:password).
  6. Server decodes the header, looks up the username in the htpasswd file, hashes the provided password, and compares it to the stored hash.
  7. 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.

/admin/.htaccessapache
AuthName "Admin Panel"
/reports/.htaccessapache
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/.htpasswd

This 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/.htpasswd

Correct approach:

AuthUserFile /etc/apache2/.htpasswd
AuthUserFile /home/username/.htpasswd

If 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:

find-path.php (delete after use)php
<?php
echo $_SERVER['DOCUMENT_ROOT'];
// Output example: /home/username/public_html

If 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:

/admin/.htaccessapache
AuthType Basic
AuthName "Admin Panel"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user

Protect a single file:

.htaccess in the directory containing the fileapache
<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-user

This 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 editor

This 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:

/admin/.htaccessapache
AuthType Basic
AuthName "Admin Panel"
AuthUserFile /etc/apache2/.htpasswd
Require user admin superadmin
/reports/.htaccessapache
AuthType Basic
AuthName "Reports"
AuthUserFile /etc/apache2/.htpasswd
Require user admin editor analyst

In 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-user

Apache 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-user

Limitations 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:password123

Always 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-user

No 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 fail2ban or similar tools at the server level to block repeated failed attempts.
Combining password protection with IP restrictionapache
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_dbd for 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.

Navigation