Htpasswd Generator→Specialized Version
πŸ”’

SHA1 htpasswd Generator

Generate {SHA} format .htpasswd entries

Unsalted and a single pass β€” the same password always yields the same hash. Only use it where a legacy system requires it.

Hashing runs in this tab and nothing is uploaded β€” but generate credentials you intend to use with htpasswd on the server rather than in a browser.

Apache configuration
<Directory "/var/www/private">
    AuthType Basic
    AuthName "Restricted Area"
    AuthUserFile /etc/apache2/.htpasswd
    Require valid-user
</Directory>

A .htaccess file is ignored unless AllowOverride permits it, so a directory can appear unprotected with no error. Keep .htpasswd outside the document root.

bcrypt is not offered here. It needs a native implementation that browsers do not provide. If your Apache supports it, htpasswd -B -c .htpasswd user is the stronger choice β€” APR1 is the portable fallback, not the best one.

The {SHA} Format

An htpasswd line in {SHA} format is the base64 of a raw SHA-1 digest, prefixed with the literal scheme name:

`` username:{SHA}qUqP5cyxm6YcTAhz05Hph5gvu9M= `

It exists for interoperability with LDAP directories, which use the same encoding, and it is supported by Apache and by nginx's auth_basic_user_file.

It Is Unsalted, and That Is the Problem

There is no salt in the format at all. Two users with the same password produce byte-identical hashes, which means:

  • A leaked file immediately shows which accounts share a password.
  • Precomputed rainbow tables apply directly, with no per-account work.
  • A single SHA-1 operation is fast, so brute force runs at billions of guesses per second.
APR1 at least salts and iterates. {SHA} does neither.

When to Use It Anyway

Only when something on the other end demands it β€” an LDAP import, a legacy appliance, a device firmware that reads no other format. If you have a choice, choose APR1; if you have a real choice, do authentication in your application with bcrypt or Argon2 and leave Basic Auth for infrastructure gating.

Using It With nginx

`nginx location /internal/ { auth_basic "Internal"; auth_basic_user_file /etc/nginx/.htpasswd; } `

nginx reads both {SHA} and $apr1$ lines from the same file, so a file can mix formats during a migration.

Compensating Controls

If you must use this format, the password must carry the security the hash does not:

  • Generate a long random password rather than choosing one.
  • Never reuse it anywhere else β€” an unsalted hash of a reused password is the worst case.
  • Serve only over HTTPS.
  • Restrict by IP as well, where the client set is known.
  • Rotate when anyone with access leaves.

Computed Locally

The digest is computed in your browser with the Web Crypto API. The password is never transmitted.

Testing It Works

`bash # Should return 401 curl -I https://staging.example.com/

# Should return 200 curl -I -u admin:password https://staging.example.com/

# What the browser actually sends printf 'admin:password' | base64 # YWRtaW46cGFzc3dvcmQ= β†’ Authorization: Basic YWRtaW46cGFzc3dvcmQ= `

That last line is the whole security model: base64, no key, reversible by anyone who sees the header. Over TLS it is fine; over plain HTTP it is equivalent to publishing the password.

Keeping Machines Out but Letting the Right Ones In

A blanket auth gate breaks things you rely on. Exempt them explicitly:

PathWhy
/.well-known/acme-challenge/Let's Encrypt renewal fails otherwise, silently, until the certificate expires
/healthzLoad balancer and uptime probes
/api/webhooks/Third-party callbacks cannot authenticate

Blocking Crawlers Properly

Basic auth keeps a staging site out of search results, and it is worth pairing with the other two signals so that nothing leaks through a link:

` # robots.txt User-agent: * Disallow: / `

` X-Robots-Tag: noindex, nofollow `

Note that robots.txt alone does not prevent indexing β€” it prevents crawling, and a URL linked from elsewhere can still appear in results without a snippet. The noindex header is what actually removes it.

CI and Scripts

`bash # curl curl -u "$STAGING_USER:$STAGING_PASS" https://staging.example.com/

# Playwright await context.setHTTPCredentials({ username, password }); ``

Keep the credentials in secrets, never in the repository β€” a password committed to git stays in the history after you delete it.

Frequently Asked Questions

Why is {SHA} htpasswd unsalted?

The format predates the practice. It was defined for LDAP interoperability, where the same encoding is used, and it stores nothing but the base64 of a raw SHA-1 digest β€” there is no field for a salt, so identical passwords always produce identical hashes.

Should I use {SHA} or $apr1$?

APR1, unless something specifically requires {SHA}. APR1 salts and iterates; {SHA} does neither, so a leaked APR1 file requires per-account work to attack while a leaked {SHA} file yields to rainbow tables directly.

Does nginx support this format?

Yes. nginx’s `auth_basic_user_file` reads both `{SHA}` and `$apr1$` entries, and a single file can contain a mix of both β€” which makes migrating from one to the other straightforward.

Related Tools

Explore other tools you might find useful:

More Htpasswd Generator tools

You might also need