Skip to content
Powrót do bloga
Bezpieczeństwo

Jak utworzyć plik .htpasswd: przewodnik po HTTP Basic Auth

Utwórz plik .htpasswd z bcrypt lub apr1, skonfiguruj HTTP Basic Auth na Apache, nginx, Dockerze i Kubernetes oraz zabezpiecz dostęp. Praktyczny przewodnik 2026.

11 min czytania

Jak utworzyć plik .htpasswd: przewodnik po HTTP Basic Auth

Plik .htpasswd to magazyn poświadczeń po stronie serwera dla HTTP Basic Authentication: zwykły plik tekstowy, w którym każda linia to jedna para username:hash. Aby utworzyć plik .htpasswd, należy wygenerować tę zahaszowaną linię i zapisać ją w miejscu, z którego serwer WWW może ją odczytać. Są trzy sposoby:

  • Polecenie htpasswd (z pakietu apache2-utils / httpd-tools) — kanoniczne narzędzie.
  • openssl passwd — dostępne niemal wszędzie, bez dodatkowego pakietu.
  • W przeglądarce — użyj generatora htpasswd, aby utworzyć wpis lokalnie, bez żadnej instalacji i bez wysyłania czegokolwiek przez sieć.

Poza samą komendą warto wiedzieć, jak działa uzgadnianie Basic Auth, jak utworzyć plik na trzy sposoby, który z pięciu formatów hash wybrać oraz jak wpiąć go w Apache, nginx, Docker, Kubernetes, Caddy i Traefik. Na koniec — jak to zabezpieczyć, żeby nie udostępnić pliku poświadczeń, który każdy może pobrać.

Czym jest plik .htpasswd?

Każda linia w pliku .htpasswd przechowuje poświadczenia jednego użytkownika jako parę rozdzieloną dwukropkiem. Nazwa użytkownika jest zapisana w niezmienionej postaci; hasło jest przechowywane wyłącznie jako jednokierunkowy hash, więc tekst jawny nigdy nie trafia na dysk. Budowa pojedynczej linii bcrypt wygląda tak:

admin    :    $2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
│             │
└─ username   └─ hash (algorithm prefix $2y$ + cost + salt + digest)

Najpierw nazwa użytkownika, potem pojedynczy :, a następnie hash. Nazwa użytkownika może mieć do 255 bajtów i nie może zawierać dwukropka, bo dwukropek jest separatorem pól. Hash niesie własny znacznik algorytmu jako prefiks ($2y$ dla bcrypt, $apr1$ dla Apache MD5, {SHA} dla SHA-1), więc serwer wie, jak go zweryfikować, bez dodatkowej konfiguracji.

Dla wielu użytkowników dodaje się jedną linię na użytkownika:

admin:$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
alice:$2y$10$3bQ8xY7tLp2mZ0xW5cR4fO9vK1jH6sD2nG8aQ5wE3rT7uI4oP1cm
bob:$apr1$mZ0xW5cR$4fK1jH6sD2nG8aQ5wE3rT2

Algorytmy można mieszać w jednym pliku. Serwer czyta każdą linię, wykrywa format na podstawie prefiksu i weryfikuje hash zgodnie z tym, czego użyto. Powyżej obok siebie stoją dwaj użytkownicy bcrypt i jeden apr1.

.htpasswd a .htaccess

Te dwa pliki bywają mylone, bo na Apache idą w parze, ale pełnią różne funkcje. .htaccess to plik konfiguracyjny Apache działający per katalog. Zawiera dyrektywy — w tym te, które włączają Basic Auth i wskazują na magazyn poświadczeń. .htpasswd to baza poświadczeń — same linie username:hash, bez konfiguracji.

W skrócie: .htaccess decyduje, że katalog wymaga logowania i gdzie znaleźć listę użytkowników; .htpasswd to ta lista użytkowników. nginx w ogóle nie używa .htaccess — jego konfiguracja Basic Auth znajduje się w bloku server lub location głównej konfiguracji, ale czyta ten sam format poświadczeń .htpasswd.

Jak działa HTTP Basic Authentication

HTTP Basic Authentication to uzgadnianie typu wyzwanie-odpowiedź wbudowane w specyfikację HTTP. Kto rozumie te trzy kroki, ten później bez trudu zdiagnozuje problem:

  1. Klient żąda chronionego zasobu bez poświadczeń.
  2. Serwer odpowiada 401 Unauthorized i dołącza nagłówek WWW-Authenticate: Basic realm="..." — to jest wyzwanie.
  3. Klient ponawia żądanie z nagłówkiem Authorization: Basic <base64(user:password)>. Jeśli poświadczenia pasują do linii w pliku .htpasswd, serwer zwraca zasób.

To cały protokół. Nie ma formularza logowania, nie ma cookie sesji, nie ma tokenu. Każde kolejne żądanie niesie ten sam nagłówek.

Wyzwanie 401 / WWW-Authenticate

Nagłówek WWW-Authenticate robi dwie rzeczy. Token Basic mówi klientowi, którego schematu użyć, a ciąg realm oznacza przestrzeń ochrony. Przeglądarki pokazują tekst realm w oknie logowania („Witryna informuje: …”) i traktują go jako klucz pamięci podręcznej: poświadczenia podane dla jednego realm trafiają do innych adresów URL w tym samym realm, więc okno logowania nie wyskakuje na każdej stronie z osobna.

Oto surowa wymiana, przechwycona przez curl -i:

$ curl -i https://example.com/admin/
HTTP/2 401
www-authenticate: Basic realm="Restricted Area"

$ curl -i -u admin:s3cret https://example.com/admin/
HTTP/2 200

Nagłówek Authorization: Basic

Poświadczenie wysyłane przez klienta to base64(username:password). I tu pada najważniejszy fakt bezpieczeństwa dotyczący Basic Auth: base64 jest kodowaniem, a nie szyfrowaniem. Każdy może je odwrócić, więc poświadczenie podróżuje w praktyce w postaci tekstu jawnego. Ten obieg w obie strony łatwo sprawdzić samodzielnie:

# Encode the credential the way a browser does
printf 'admin:s3cret' | base64
# → YWRtaW46czNjcmV0

# Anyone who captures the header can decode it instantly
printf 'YWRtaW46czNjcmV0' | base64 -d
# → admin:s3cret

Właśnie ta odwracalność wymusza, by Basic Auth działało przez HTTPS — bez TLS hasło jest czytelne dla każdego na trasie sieciowej. Do ręcznego zbudowania lub odczytania tego nagłówka przyda się koder/dekoder Base64, który wykonuje tę samą transformację user:password w przeglądarce.

Jak utworzyć plik .htpasswd

Plik można utworzyć na trzy praktyczne sposoby. Wybór zależy od tego, co jest zainstalowane i gdzie hasło ma się znaleźć.

Za pomocą polecenia htpasswd

Plik wykonywalny htpasswd jest dostarczany w pakiecie narzędziowym Apache. Najpierw go zainstaluj:

# Debian / Ubuntu
sudo apt install apache2-utils

# RHEL / CentOS / Fedora
sudo yum install httpd-tools

Utwórz plik i jego pierwszego użytkownika. Flaga -c oznacza create i nadpisze istniejący plik — używaj jej tylko za pierwszym razem:

htpasswd -c /etc/nginx/.htpasswd admin
# prompts twice for the password, then writes the file

Aby dodać kolejnych użytkowników, pomiń -c, tak aby dopisywać zamiast nadpisywać:

htpasswd /etc/nginx/.htpasswd alice

Aby wymusić bcrypt zamiast domyślnego dla platformy, dodaj -B. Aby wypisać wpis na stdout bez dotykania pliku (przydatne przy przesyłaniu do konfiguracji lub Dockerfile), połącz -b (hasło w wierszu poleceń) i -n (bez pliku):

htpasswd -Bbn admin 's3cret'
# → admin:$2y$10$N9qo8uLOickgx2ZMRZoMye...

Flagi, których faktycznie użyjesz:

FlagaZnaczenie
-cUtwórz nowy plik (nadpisuje, jeśli istnieje) — tylko pierwszy użytkownik
-BUżyj bcrypt
-bPobierz hasło jako argument wiersza poleceń (bez pytania)
-nWypisz na stdout zamiast zapisywać plik
-DUsuń wskazanego użytkownika z pliku

Jedno zastrzeżenie do -b: hasło trafia do historii powłoki. Dla poświadczeń produkcyjnych lepiej sprawdzi się forma z pytaniem albo opcja w przeglądarce opisana niżej.

Bez apache2-utils — za pomocą OpenSSL

Brak pliku wykonywalnego htpasswd? OpenSSL jest praktycznie w każdym systemie i potrafi wprost wytworzyć hash apr1. Opakuj go w printf, aby zbudować kompletną linię:

printf "admin:$(openssl passwd -apr1 's3cret')\n" >> /etc/nginx/.htpasswd
# admin:$apr1$k3l4Hj9.$qN8vY7tLp2mZ0xW5cR4f.

Format apr1 jest przenośny między Apache i nginx, więc na minimalnej maszynie to droga, która nie wymaga żadnych dodatkowych zależności.

W przeglądarce — bez instalacji, bez wycieku

Jeśli nie chcesz instalować pakietu albo wolisz nie wpisywać hasła produkcyjnego do powłoki, gdzie wyląduje w ~/.bash_history, wygeneruj wpis po stronie klienta. Generator htpasswd oblicza hashe bcrypt, apr1 i SHA-1 w całości w przeglądarce, podaje gotową do wklejenia linię user:hash wraz z pasującym blokiem konfiguracji serwera i niczego nie przesyła przez sieć. Przy okazji warto utworzyć silne, unikalne hasło w generatorze haseł, zamiast używać istniejącego ponownie.

Porównanie formatów haseł htpasswd

Polecenie htpasswd może wyemitować pięć formatów, a nie są one sobie równe. Tabela poniżej pomaga przy wyborze:

FormatPrefiksZ soląSiłaZastosowanie
bcrypt$2y$TakNajsilniejszyApache, Docker Registry, Caddy, Traefik
apr1 (Apache MD5)$apr1$TakUmiarkowanynginx (przenośny, bezpieczny domyślny)
SHA-1{SHA}NieSłabyTylko zgodność ze starszymi systemami
crypt (DES)(brak)Tak (2 znaki)Bardzo słabyNie używać
plain(brak)NieBrakTylko lokalne testy

Kilka uwag, które nie zmieszczą się w komórce tabeli. bcrypt używa losowej 16-bajtowej soli i adaptacyjnego współczynnika kosztu (domyślnie 10, współczesna rekomendacja 12), więc identyczne hasła dają różne hashe, a nakład pracy rośnie razem z mocą sprzętu. Ma jedną osobliwość: obcina hasło do 72 bajtów, a wszystko dłuższe po cichu ignoruje. apr1 wykonuje 1 000 rund osolonego MD5. To wyraźnie słabsze od bcrypta, ale działa natywnie i w Apache, i w nginx, dlatego nadaje się jako wybór przenośny. SHA-1 nie ma soli, więc identyczne hasła dają identyczne skróty i działają na nim tęczowe tablice — zostaw go wyłącznie dla starszych systemów. crypt i plain zostały z powodów historycznych i testowych; do produkcji nie pasuje żaden.

Prefiksy $2a$ / $2b$ / $2y$

Hashe bcrypt zaczynają się od $2a$, $2b$ lub $2y$. To ten sam algorytm dający równoważne, wymienne hashe; litery wersji zostały po historycznych poprawkach błędów w tym, jak niektóre biblioteki obsługiwały znaki z ustawionym najwyższym bitem oraz długość ciągów. Apache htpasswd emituje $2y$, a Caddy, Traefik i Docker Registry poprawnie go weryfikują.

Głębsze porównanie bcrypta z nowoczesnymi alternatywami daje przewodnik bcrypt vs Argon2 vs scrypt — opisuje, czym te algorytmy haszowania haseł różnią się pod względem kosztu, twardości pamięciowej i modelu zagrożeń.

Konfiguracja Basic Auth na serwerze

Sam plik poświadczeń nic nie robi — serwerowi trzeba jeszcze powiedzieć, że ma go wymagać. Poniżej sześć platform.

Apache (.htaccess)

Wstaw to do pliku .htaccess w katalogu, który chcesz chronić (lub do bloku <Directory> w vhost):

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

AuthName to ciąg realm pokazywany przez przeglądarkę, AuthUserFile to bezwzględna ścieżka do pliku poświadczeń, a Require valid-user akceptuje każdego użytkownika z tej listy.

nginx (auth_basic)

nginx umieszcza konfigurację w bloku location lub server — nie ma .htaccess:

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

Przeładuj poleceniem nginx -s reload. Tutaj sięgnij po format apr1. nginx deleguje weryfikację bcrypt do systemowego crypt(), co zawodzi w wielu kompilacjach (więcej o tym w sekcji o błędach), a apr1 weryfikuje wewnętrznie na każdej platformie.

Docker Registry i Kubernetes ingress-nginx

Backend htpasswd prywatnego Docker Registry akceptuje wyłącznie bcrypt. Wygeneruj wpis, zamontuj plik i wskaż na niego registry:

# Generate a bcrypt entry into a file
htpasswd -Bbn admin 's3cret' > auth/htpasswd

# Run the registry with that file
docker run -d -p 5000:5000 \
  -v "$(pwd)/auth:/auth" \
  -e REGISTRY_AUTH=htpasswd \
  -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  registry:2

W przypadku Kubernetes ingress-nginx zapisz plik jako Secret i odwołaj się do niego przez adnotacje:

kubectl create secret generic basic-auth --from-file=auth=./auth/htpasswd
metadata:
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"

Uwaga: klucz Secret musi nazywać się auth — ingress-nginx szuka dokładnie tego klucza.

Caddy i Traefik

Oba oczekują hashy bcrypt. Caddy używa dyrektywy basic_auth (wklej hash bcrypt, nie tekst jawny):

example.com {
    basic_auth /admin/* {
        admin $2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
    }
}

Traefik używa middleware basicauth, z parami user:bcrypt-hash (zaeskejpuj każdy $ zgodnie z formatem swojej konfiguracji):

http:
  middlewares:
    admin-auth:
      basicAuth:
        users:
          - "admin:$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"

Gdy punkt końcowy jest już chroniony, sprawdź jego działanie z wiersza poleceń. Kreator poleceń cURL składa żądanie -u user:pass, co pozwala potwierdzić zarówno 401, jak i uwierzytelnione 200.

Najlepsze praktyki bezpieczeństwa

Basic Auth jest proste, więc nieliczne sposoby na jego nadużycie łatwo wychwycić.

  • Zawsze serwuj przez HTTPS. Poświadczenie to odwracalne base64, więc zwykłe HTTP ujawnia hasło w transmisji. TLS należy terminować przed każdym chronionym punktem końcowym, bez wyjątków.
  • Przechowuj plik poza katalogiem głównym WWW. Jeśli .htpasswd leży w serwowanym katalogu, błędna konfiguracja może pozwolić komuś go pobrać. Trzymaj go gdzieś w stylu /etc/nginx/.htpasswd, ustaw chmod 640 i nadaj właściciela na użytkownika serwera WWW (www-data, nginx), aby plik mógł odczytać serwer, a inne konta już nie.
  • Używaj silnych, unikalnych haseł. Każde konto powinno dostać własne hasło o wysokiej entropii z generatora haseł, nigdy używane ponownie. Co znaczy „wystarczająco silne” w bitach, rozkłada na czynniki wyjaśnienie entropii hasła.
  • Znaj jego ograniczenia. Basic Auth nie ma wylogowania ani sesji: przeglądarka buforuje poświadczenie per realm aż do zamknięcia i wysyła je ponownie przy każdym żądaniu. Szerszą listę kontrolną dotyczącą haszowania, nagłówków i walidacji daje wpis o najlepszych praktykach bezpieczeństwa webowego.

Rozwiązywanie typowych błędów

nginx: crypt_r() failed (22: Invalid argument)

To najczęstsza awaria nginx Basic Auth i zawsze oznacza to samo: nginx próbował zweryfikować hash bcrypt ($2y$) na libc, które nie zawiera schematu Blowfish — zazwyczaj musl w Alpine albo starszy glibc. Rozwiązaniem jest ponowne wygenerowanie wpisu jako apr1, który nginx weryfikuje wewnętrznie na dowolnej platformie:

printf "admin:$(openssl passwd -apr1 's3cret')\n" > /etc/nginx/.htpasswd
nginx -s reload

Przejście na obraz bazowy, którego libc obsługuje bcrypt, też działa, ale apr1 jest prostsze i bardziej przenośne.

401 mimo poprawnego hasła

Gdy hasło jest na pewno poprawne, a mimo to wraca 401, przejdź tę listę kontrolną po kolei:

  1. Ścieżka pliku. Potwierdź, że AuthUserFile / auth_basic_user_file wskazuje na rzeczywisty plik (ścieżka bezwzględna, bez literówek).
  2. Uprawnienia. Użytkownik serwera WWW musi móc odczytać plik. Sprawdź to poleceniem sudo -u www-data cat /etc/nginx/.htpasswd.
  3. Końce linii / kodowanie. Plik edytowany w systemie Windows może nieść znaki \r, które psują hash. Uruchom file .htpasswd i w razie potrzeby przepuść go przez dos2unix.
  4. Nieaktualna pamięć podręczna przeglądarki. Przeglądarka buforuje poświadczenia per realm. Przetestuj w oknie prywatnym/incognito, aby wykluczyć zapamiętane stare hasło.
  5. Niezgodność hasha. Sprawdź, czy zapisany hash faktycznie pasuje do hasła — wklej oba w tryb weryfikacji generatora htpasswd, zanim obwinisz konfigurację.

Kiedy NIE używać Basic Auth

Basic Auth pasuje do wąskiego zestawu zadań: witryny staging, wewnętrznej ścieżki admina, punktu końcowego z artefaktami CI, pulpitu metryk, prywatnego registry. Nie ma zależności, a konfiguracja zajmuje dwie minuty.

Jest za to złym narzędziem do logowania w produkcie. Nie ma wylogowania, resetu hasła, ograniczania liczby żądań, blokady konta ani MFA. Poświadczenia wracają przy każdym żądaniu i zostają w pamięci przeglądarki aż do jej zamknięcia. Wszędzie tam, gdzie użytkownicy się logują, lepiej sprawdzą się sesje, OAuth lub OIDC. To rozróżnienie utrzymuje Basic Auth użytecznym dokładnie tam, gdzie naprawdę pasuje.

FAQ

Co właściwie zawiera pojedyncza linia w pliku .htpasswd?

Parę username:hash rozdzieloną dwukropkiem. Hash zaczyna się od prefiksu algorytmu ($2y$ dla bcrypt, $apr1$ dla Apache MD5, {SHA} dla SHA-1), a po nim idą sól i skrót. Hasło w postaci tekstu jawnego nigdy nie trafia do pliku.

Jaka jest różnica między .htpasswd a .htaccess?

.htaccess to plik konfiguracyjny Apache działający per katalog — włącza Basic Auth i wskazuje na magazyn poświadczeń. .htpasswd to ten magazyn poświadczeń, zawierający linie username:hash. nginx używa formatu .htpasswd, ale konfiguruje uwierzytelnianie w blokach server/location, a nie w .htaccess.

Jak dodać, zmienić lub usunąć użytkownika w pliku .htpasswd?

Aby dodać lub zmienić użytkownika, uruchom htpasswd /path/.htpasswd username bez -c — jeśli użytkownik istnieje, jego hash zostaje zaktualizowany. Aby usunąć użytkownika, uruchom htpasswd -D /path/.htpasswd username. Flagi -c używaj tylko dla pierwszego użytkownika, bo nadpisuje cały plik.

Jak przeglądarka pamięta poświadczenia Basic Auth i jak użytkownicy się wylogowują?

Przeglądarka buforuje poświadczenia kluczowane przez realm i automatycznie wysyła je ponownie przy każdym pasującym żądaniu. Nie ma standardowego wylogowania: poświadczenia czyści się tylko przez zamknięcie przeglądarki albo wyczyszczenie jej pamięci podręcznej. Ten brak wylogowania jest jednym z powodów, dla których Basic Auth nie nadaje się do uwierzytelniania w produkcie.

Czy można użyć tego samego pliku .htpasswd dla Apache i nginx?

Tak, o ile format hash obsługują oba. apr1 (Apache MD5) jest weryfikowany natywnie przez Apache i nginx wszędzie, więc to najbezpieczniejszy wspólny wybór. bcrypt działa na Apache, ale na nginx zależy od systemowego crypt(), które zawodzi na kompilacjach Alpine/musl.

Czy HTTP Basic Authentication wciąż ma znaczenie w 2026 roku?

Tak. Jako lekka bramka na szczycie HTTPS — narzędzia wewnętrzne, środowiska staging, prywatne registry, punkty końcowe monitorowania — wciąż jest praktyczna i nie ciągnie za sobą żadnych zależności. Nie należy jej tylko mylić z uwierzytelnianiem w produkcie skierowanym do użytkownika, które potrzebuje sesji, resetów, ograniczania liczby żądań i MFA, czego Basic Auth nie zapewnia.

Zweryfikowane przez zespół Go Tools: każde polecenie, blok konfiguracji i format hash w tym przewodniku sprawdzono względem wzorcowego wyniku Apache htpasswd (apache2-utils) oraz OpenSSL.

Tagi: htpasswd basic-auth http-authentication nginx apache bcrypt security

Powiązane artykuły

Zobacz wszystkie artykuły