HTTP Basic Authentication with Nginx

Blog / Server Management · February 12, 2021 · Updated June 10, 2026 · 6 min read
HTTP Basic Authentication with Nginx

HTTP Basic Authentication is the simplest way to put a username-and-password gate in front of a website or a single URL path. The browser shows a native login popup, sends the credentials on every request, and Nginx checks them against a password file before serving anything. There's no login page to build, no session store, and no application code — it's a few lines of Nginx config.

That simplicity is exactly why it's still useful in 2026. It is not a replacement for real application login, but it's a fast, reliable lock for the things that shouldn't be public.

In this guide you'll learn when Basic Auth is the right tool, how to create a password file with htpasswd (using bcrypt), how to wire it into Nginx at the server or location level, and how to combine it with IP allowlists without breaking your TLS certificate renewals.

When to use HTTP Basic Auth (and when not)

Basic Auth is a great fit for protecting infrastructure and pre-production surfaces:

  • Staging and preview environments you don't want indexed or probed.
  • Admin panels and internal dashboards (Grafana, Kibana, custom tools).
  • Metrics and health endpoints such as Prometheus /metrics that leak internals.
  • Holding pages for a site that isn't launched yet.

It is the wrong tool for end-user accounts. Keep these limits in mind:

  • Credentials are Base64-encoded, not encrypted. Anyone who can read the request sees them in plain text, so Basic Auth must run behind HTTPS/TLS. If you haven't set that up yet, start with Configure SSL with Let's Encrypt and Nginx.
  • There's no logout, no password reset, no roles, and no rate limiting of guesses. It's a single shared gate, not user management.
  • It is not a substitute for app-level auth. For customer-facing login use real sessions, OAuth, or SSO.

Step 1: Install the htpasswd tool

Passwords live in a small text file that you generate with the htpasswd utility. It doesn't ship with Nginx, so install the package that provides it for your distro — apache2-utils on Debian/Ubuntu, or httpd-tools on RHEL, AlmaLinux, Rocky, and Fedora. You do not need to install Apache itself.

# Debian / Ubuntu
sudo apt update
sudo apt install apache2-utils

# RHEL / AlmaLinux / Rocky / Fedora
sudo dnf install httpd-tools

Step 2: Create the password file

Create the file and add your first user. The -c flag creates the file, so use it only the very first time — running it again with -c overwrites the file and wipes existing users. Add -B to hash the password with bcrypt, which is far stronger than the legacy MD5/crypt defaults and is supported by modern Nginx and htpasswd.

Store the file outside your web root. /etc/nginx/.htpasswd is the conventional location.

# First user — creates the file (-c) with a bcrypt hash (-B)
sudo htpasswd -c -B /etc/nginx/.htpasswd alice

# Add more users later — NO -c, or you'll erase the file
sudo htpasswd -B /etc/nginx/.htpasswd bob

# Remove a user
sudo htpasswd -D /etc/nginx/.htpasswd bob

Each line in the file is username:hashed_password. If you're scripting on a host that doesn't have htpasswd installed, openssl can produce a compatible bcrypt or APR1 entry that you append yourself:

# Generate a bcrypt entry and append it to the file
printf 'carol:%s\n' "$(openssl passwd -apr1)" | sudo tee -a /etc/nginx/.htpasswd

# openssl prompts for the password, then prints the hash

Step 3: Configure Nginx

Add two directives to the server block (to protect the whole site) or a single location block (to protect one path). auth_basic sets the realm text shown in the browser prompt and turns auth on; auth_basic_user_file points at the password file you just created.

server {
    listen 443 ssl;
    server_name staging.example.com;

    # ...your ssl_certificate / ssl_certificate_key lines...

    location / {
        auth_basic           "Restricted Area";
        auth_basic_user_file /etc/nginx/.htpasswd;

        # your normal proxy_pass / try_files / root config
        try_files $uri $uri/ =404;
    }
}

Always test the configuration before reloading — nginx -t catches typos and bad paths so you don't take the site down. Then reload (a reload applies the new config with zero downtime; a restart isn't needed).

sudo nginx -t
sudo systemctl reload nginx

Now visit the site: the browser prompts for a username and password. Correct credentials let the request through; wrong or missing ones return 401 Authorization Required.

Combine Basic Auth with IP allowlists

A common pattern is: let the office or VPN in without a prompt, but require a password from everywhere else. Nginx's satisfy directive controls how allow/deny rules and auth_basic combine.

  • satisfy any; — access is granted if either the IP matches or the password is correct. Good for "trusted network bypasses the prompt."
  • satisfy all; — the client must pass both the IP check and Basic Auth. Use this to lock a path to known IPs and require a password.
location /admin/ {
    satisfy any;

    # Trusted networks skip the password prompt
    allow 203.0.113.0/24;   # office range
    allow 198.51.100.7;     # VPN gateway
    deny  all;

    auth_basic           "Admin Only";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

Keep Let's Encrypt renewals working

If you put Basic Auth on the whole server, the HTTP-01 challenge that Let's Encrypt uses to renew certificates can start failing — the certificate authority hits /.well-known/acme-challenge/ and gets a 401 instead of the challenge file. Exclude that path by turning auth off for it with auth_basic off;.

server {
    listen 80;
    server_name staging.example.com;

    # Let the ACME challenge through without a password
    location /.well-known/acme-challenge/ {
        auth_basic off;
        allow all;
        root /var/www/html;
    }

    location / {
        auth_basic           "Restricted Area";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }
}

Security best practices

  • Always serve over HTTPS. Basic Auth credentials travel in (decodable) Base64 on every request — without TLS they're effectively plain text.
  • Use bcrypt (htpasswd -B). Avoid the old MD5/crypt hashes for new files.
  • Lock down the password file so only root and Nginx can read it.
  • Rotate credentials when staff leave or a shared password has been passed around too widely; remove users with htpasswd -D.
  • Don't reuse production passwords for a staging gate.
  • Graduate to real auth — OAuth, SSO, or session-based login — for anything user-facing.

Tighten the file permissions like this:

sudo chown root:www-data /etc/nginx/.htpasswd
sudo chmod 640 /etc/nginx/.htpasswd

Wrapping up

HTTP Basic Auth with Nginx is a five-minute lock for staging sites, admin panels, and metrics endpoints: install htpasswd, generate a bcrypt password file, add two directives, and reload. Pair it with HTTPS and IP allowlists and it becomes a solid first line of defence for everything that shouldn't be public — just remember it complements, rather than replaces, application-level authentication.

Keep tuning your stack with 301 redirections in Nginx and a high-performance Django on Nginx with uWSGI setup. If you'd rather hand off server hardening and infrastructure entirely, our AWS consulting services team can help.

Frequently Asked Questions

Is HTTP Basic Authentication secure?

It's only as secure as the transport underneath it. Credentials are Base64-encoded, not encrypted, so anyone who can read the traffic can recover them. Over HTTPS with bcrypt-hashed passwords it's perfectly adequate for gating staging sites, admin panels, and internal tools — but it offers no logout, password reset, roles, or brute-force protection, so it isn't suitable for end-user accounts.

What's the difference between .htpasswd and .htaccess?

.htpasswd is the file that stores username:hash pairs. .htaccess is an Apache-specific per-directory config file — Nginx does not read .htaccess at all. In Nginx you point at the password file directly with the auth_basic_user_file directive inside your server or location block.

How do I add or remove users from the password file?

To add a user, run htpasswd -B /etc/nginx/.htpasswd username without the -c flag (the -c flag recreates the file and deletes existing users). To remove a user, run htpasswd -D /etc/nginx/.htpasswd username. No Nginx reload is needed — the file is read on each request.

Why do my Let's Encrypt renewals fail after enabling Basic Auth?

The HTTP-01 challenge fetches a file from /.well-known/acme-challenge/, and a site-wide auth_basic returns 401 for it. Add a dedicated location /.well-known/acme-challenge/ block with auth_basic off; and allow all; so the certificate authority can reach the challenge while the rest of the site stays protected.

Can I use HTTP Basic Auth for production user login?

No. It has no per-user sessions, logout, password reset, account lockout, or roles, and the browser caches credentials until the tab closes. Use it for infrastructure and pre-production gates; build customer-facing login on sessions, OAuth, or SSO instead.

How do I let trusted IPs skip the password prompt?

Use the satisfy any; directive together with allow/deny rules and auth_basic. With satisfy any, a request is accepted if the IP is allowed or the password is correct, so your office or VPN range gets in without a prompt while everyone else must authenticate. Use satisfy all; when you want both checks to pass.

Share this article