How to Set Up Password Protection for a Directory in Apache

How to set up password protection for a directory

Every staging site I’ve ever built eventually needs a quick way to keep it out of casual view — before there’s a real login system, before it’s ready for the public, sometimes just to keep search engines from indexing a half-finished page. Apache’s built-in Basic Authentication, backed by mod_authn_file and htpasswd, has been my go-to for this since I don’t need to write a single line of application code to set it up.

Here’s exactly how I configure it, plus a stronger alternative when I want it.

How Apache Basic Authentication Works

When a directory is protected, Apache responds to unauthenticated requests with a 401 Unauthorized and a WWW-Authenticate header. The browser prompts for a username and password, then re-sends it (base64-encoded, not encrypted) with each subsequent request — which is exactly why I never do this without HTTPS.

Prerequisites

  • Apache installed and running
  • Root or sudo access, or .htaccess access with the right AllowOverride settings
  • mod_authn_file, mod_authz_user, and mod_auth_basic enabled (default in most installs)
  • The htpasswd utility (apache2-utils on Debian/Ubuntu, httpd-tools on RHEL/CentOS)
sudo apt install apache2-utils      # Debian/Ubuntu
sudo dnf install httpd-tools         # RHEL/CentOS

Step 1: Create a Password File

I always create .htpasswd outside the web-servable document root so it can never be downloaded directly:

sudo htpasswd -c /etc/apache2/.htpasswd ahmad

That prompts for a password. The -c flag creates a new file — I drop it when adding more users to an existing file:

sudo htpasswd /etc/apache2/.htpasswd another_user

I check the file (hashed, never plaintext):

cat /etc/apache2/.htpasswd
ahmad:$apr1$eS3f8sd2$K9z...hashedvalue

Step 2: Configure Apache to Require Authentication

Option A: Inside the Virtual Host (my preference)

<Directory /var/www/html/admin>
    AuthType Basic
    AuthName "Restricted Area"
    AuthUserFile /etc/apache2/.htpasswd
    Require valid-user
</Directory>
  • AuthType Basic — HTTP Basic Authentication.
  • AuthName — the realm shown in the browser’s login prompt.
  • AuthUserFile — the password file I created above.
  • Require valid-user — any user in the password file can authenticate.

I reload Apache:

sudo apachectl configtest
sudo systemctl reload apache2

Option B: Using .htaccess

When I don’t have access to the main config, I place this in a .htaccess file inside the directory:

AuthType Basic
AuthName "Restricted Area"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user

This only works if AllowOverride AuthConfig (or All) is set on the parent directory:

<Directory /var/www/html/admin>
    AllowOverride AuthConfig
</Directory>

.htaccess is convenient, but I prefer the virtual host method when I have server config access, since Apache re-reads .htaccess on every request.

Step 3: Test It

curl -I http://example.com/admin/

I expect HTTP/1.1 401 Unauthorized, then test with credentials:

curl -I -u ahmad:yourpassword http://example.com/admin/

I expect HTTP/1.1 200 OK.

Restricting Access to Specific Users

If I want only certain named users, not anyone in the password file:

Require user ahmad admin_backup

Combining Authentication with IP Restrictions

I sometimes require both a valid login and a trusted network location using RequireAll/RequireAny (Apache 2.4+):

<Directory /var/www/html/admin>
    AuthType Basic
    AuthName "Restricted Area"
    AuthUserFile /etc/apache2/.htpasswd

    <RequireAll>
        Require valid-user
        Require ip 203.0.113.0/24
    </RequireAll>
</Directory>

That means a visitor has to both authenticate and connect from an allowed IP range.

Using Digest Authentication (Stronger Alternative)

Basic Authentication sends credentials base64-encoded (trivially decodable) on every request, relying entirely on HTTPS for protection. Digest Authentication hashes credentials before transmission, adding a layer of protection even without TLS — though I still use HTTPS regardless.

sudo htdigest -c /etc/apache2/.htdigest "Restricted Area" ahmad
<Directory /var/www/html/admin>
    AuthType Digest
    AuthName "Restricted Area"
    AuthDigestProvider file
    AuthUserFile /etc/apache2/.htdigest
    Require valid-user
</Directory>

Requires mod_auth_digest:

sudo a2enmod auth_digest

In practice I mostly just pair Basic Authentication with HTTPS rather than reaching for Digest, since HTTPS already protects credentials in transit and I need it for other reasons anyway.

Real-World Use Cases

  • Protecting a staging/QA site from public and search engine access.
  • Restricting an internal admin panel that doesn’t have its own login system.
  • Gating early-access or beta content shared with a limited group.
  • Protecting internal documentation, build artifacts, or file drop directories.

Mistakes I’ve Made

  • Storing .htpasswd inside the web-servable document root, which risks it being downloaded if something else gets misconfigured.
  • Using Basic Authentication over plain HTTP, exposing credentials to anyone watching network traffic.
  • Forgetting AllowOverride AuthConfig when relying on .htaccess, so the directive got silently ignored.
  • Reusing the same password across multiple protected directories/users, weakening the whole setup.
  • Not removing old accounts from .htpasswd when a collaborator no longer needed access.

Security Best Practices

  • I always pair Basic Authentication with HTTPS. Without TLS, credentials are trivially recoverable by anyone monitoring the connection.
  • I store the password file outside the document root with restrictive permissions: sudo chmod 640 /etc/apache2/.htpasswdsudo chown root:www-data /etc/apache2/.htpasswd
  • Strong, unique passwords per user — htpasswd supports bcrypt hashing with -B: sudo htpasswd -B -c /etc/apache2/.htpasswd ahmad
  • For anything beyond casual internal access control, I move to a proper authentication system (OAuth, SSO) rather than leaning on Basic Auth long-term.
  • I rate-limit login attempts at the firewall level, since Basic Auth has no built-in brute-force protection of its own.

Performance Considerations

  • .htaccess-based authentication gets re-parsed on every request; for high-traffic protected directories I move the rule into the main virtual host config to skip that repeated filesystem lookup.
  • Password file lookups are fast for small user lists but don’t scale well past a few dozen accounts — for larger user bases I’d reach for mod_authn_dbm or an external auth provider.
  • Authentication overhead is minimal compared to typical page rendering time, and rarely a real bottleneck for small to medium sites.

Troubleshooting

No login prompt appears, page loads directly I confirm the <Directory> block matches the actual requested path, and that mod_auth_basic is enabled:

apache2ctl -M | grep auth_basic

“Internal Server Error” after adding auth config Usually a syntax error or a missing/mispathed AuthUserFile:

sudo apachectl configtest
sudo tail -f /var/log/apache2/error.log

Correct password rejected I check for trailing whitespace accidentally copied into the password, and confirm .htpasswd uses a hash format Apache’s built-in modules support (the default htpasswd output works fine).

FAQs

Is Basic Authentication secure enough for sensitive data? Only combined with HTTPS. Even then, I’d only use it for low-to-moderate sensitivity use cases like staging sites or internal tools — for anything more sensitive, I use a full application-level auth system.

Can I protect an entire site, not just one directory? Yes — apply the same AuthType/AuthUserFile/Require valid-user block to the <Directory> matching the document root.

Do I need a separate password file for each protected directory? Not necessarily — I often reuse one .htpasswd across multiple directories and use Require user to control access per directory.

Summary and Key Takeaways

  • Apache’s built-in Basic Authentication, backed by htpasswd, is my fastest way to password-protect a directory without any application code.
  • I store the password file outside the document root with tight permissions.
  • I always pair Basic Authentication with HTTPS.
  • Digest Authentication offers stronger credential transmission protection, but I mostly just rely on Basic Auth plus TLS.
  • For anything beyond internal/staging use, I move to a full application-level authentication system.

References

  • Apache Authentication and Authorization Guide: https://httpd.apache.org/docs/current/howto/auth.html
  • Apache mod_auth_basic Documentation: https://httpd.apache.org/docs/current/mod/mod_auth_basic.html
  • Apache mod_auth_digest Documentation: https://httpd.apache.org/docs/current/mod/mod_auth_digest.html
  • htpasswd Manual: https://httpd.apache.org/docs/current/programs/htpasswd.html
Total
1
Shares

Leave a Reply

Previous Post
How to configure Apache to use SSL (HTTPS)

How to Configure Apache to Use SSL (HTTPS)

Next Post
How to enable directory listings in Apache

How to Enable Directory Listings in Apache

Related Posts