How Handle Htpasswd in NGINX

By default nginx server does not come with htpasswd, but we can generate a password file with an external tool :)

Type of cryptography in htpasswd

Bcrypt is up to 72 characters, nginx basic authentication depends on system crypt.3 and it depends on the system whether it supports bcrypt.

There is also a patch that supports bcrypt by nginx alone regardless of the OS implementation but is unverified. Some Linux doesn’t support bcrypt, so if you want to use nginx basic authentication on your system, just use SHA512 based hashes.

SHA512 is also supported by the new version even in the Apache version of htpasswd, but if it is not supported, it can be generated with the openssl command as shown below, or it can be generated with crypt.3.


  • For Python

Few words about bcrypt

More information about where 2x prefix are used in BCrypt we can find in link below:

It we think about bcrypt cost, in our case prefix is $2y$12$. This $12$ is called the cost of bcrypt, or rounds, and it affects the strength of bcrypt. If we raise the value of this cost then we have:

  • Increased strength makes it more resistant to offline attacks when htpasswd data is stolen
  • On the other hand, the risk of DoS attacks increases because the consumption of computational resources increases.

Reference about bcrypt cost


Apache — htpasswd

root@vagrant:/home/vagrant# htpasswd -nbB -C 12 example 'P@ssw0rd_123'

htpasswd in docker

htpasswd in nodejs

root@vagrant:/home/vagrant# npm install -g htpasswd
/usr/local/bin/htpasswd -> /usr/local/lib/node_modules/htpasswd/bin/htpasswd
└─┬ htpasswd@2.4.4
├─┬ apache-crypt@1.2.4
│ └── unix-crypt-td-js@1.1.4
├── apache-md5@1.1.5
├── bcryptjs@2.4.3
├── commander@2.20.3
└─┬ prompt@1.1.0
├── colors@1.4.0
├─┬ read@1.0.7
│ └── mute-stream@0.0.8
├── revalidator@0.1.8
├─┬ utile@0.3.0
│ ├── async@0.9.2
│ ├── deep-equal@0.2.2
│ ├── i@0.3.6
│ ├─┬ mkdirp@0.5.5
│ │ └── minimist@1.2.5
│ ├── ncp@1.0.1
│ └─┬ rimraf@2.7.1
│ └─┬ glob@7.1.6
│ ├── fs.realpath@1.0.0
│ ├─┬ inflight@1.0.6
│ │ └── wrappy@1.0.2
│ ├── inherits@2.0.4
│ ├─┬ minimatch@3.0.4
│ │ └─┬ brace-expansion@1.1.11
│ │ ├── balanced-match@1.0.0
│ │ └── concat-map@0.0.1
│ ├── once@1.4.0
│ └── path-is-absolute@1.0.1
└─┬ winston@2.4.5
├── async@1.0.0
├── colors@1.0.3
├── cycle@1.0.3
├── eyes@0.1.8
├── isstream@0.1.2
└── stack-trace@0.0.10
root@vagrant:/home/vagrant# htpasswd -nbBC 12 example 'P@ssw0rd_123'

htpasswd in PHP


root@vagrant:/home/vagrant# wget
--2021-02-09 17:18:21--
Resolving (
Connecting to (||:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 170 [text/plain]
Saving to: ‘htpasswd.php’
htpasswd.php 100%[===============================================================================>] 170 --.-KB/s in 0s2021-02-09 17:18:22 (7.27 MB/s) - ‘htpasswd.php’ saved [170/170]root@vagrant:/home/vagrant# php htpasswd.php

htpasswd in Python


root@vagrant:/home/vagrant# wget^C
root@vagrant:/home/vagrant# wget
--2021-02-09 17:30:08--
Resolving (
Connecting to (||:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 375 [text/plain]
Saving to: ‘’ 100%[===============================================================================>] 375 --.-KB/s in 0s2021-02-09 17:30:09 (11.8 MB/s) - ‘’ saved [375/375]root@vagrant:/home/vagrant# chmod +x
root@vagrant:/home/vagrant# ./ -C 12 example P@ssw0rd_123

DevOps Consultant. I’m strongly focused on automation, security, and reliability.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store