Skip to content

HTTP Basic Authentication

How the browser and the server exchange a username and password through the Authorization header, and why HTTPS is mandatory.

Interactive diagram — zoom in, zoom out or export an image from the diagram toolbar. Open full screen

What is Basic Auth

HTTP Basic Authentication (specified in RFC 7617) is the simplest authentication scheme in HTTP: the browser sends the username and password in the Authorization header of every request.

There is no login form, no session and no cookie. The server does not remember who has logged in: for each incoming request it reads the header, checks the credentials from scratch and only then responds. That makes Basic Auth a stateless mechanism.

How it works

The diagram above is split into three parts.

1. The first request is rejected

The browser does not know the page needs a login, so it sends a normal request. The server answers 401 Unauthorized with a WWW-Authenticate header naming the scheme and the realm (the name of the protected area):

GET /admin HTTP/1.1
Host: example.com

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Admin", charset="UTF-8"

When it sees Basic in WWW-Authenticate, the browser shows a dialog asking for a username and password. The browser draws this dialog itself; the website cannot style it.

2. Sending the credentials

The user types toby / secret. The browser joins them into toby:secret, Base64-encodes that into dG9ieTpzZWNyZXQ=, and repeats the same request with the header:

GET /admin HTTP/1.1
Host: example.com
Authorization: Basic dG9ieTpzZWNyZXQ=

The server decodes the Base64, splits at the first : and checks the pair against its credential store (an htpasswd file, a users table…). A match returns 200 OK. A mismatch returns 401 again, and the browser shows the dialog again.

3. Subsequent requests

The browser automatically resends the Authorization header with every request to the same realm, so the user does not have to type it again. The server still checks the credentials on every request.

How the Authorization header is built

Authorization: Basic base64( username + ":" + password )

Try it with the Base64 Encode / Decode tool: enter toby:secret, click Encode and you get dG9ieTpzZWNyZXQ=.

A few things to remember:

  • The username must not contain a :, because the server splits at the first :. The password may.
  • charset="UTF-8" in WWW-Authenticate tells the browser to encode non-ASCII credentials as UTF-8. Without it, browsers may each pick a different encoding.
  • Base64 is only a way to represent bytes as header-safe characters, not encryption. Anyone who captures the header can decode the password instantly.

Trying it with curl

# curl builds the Authorization header for you
curl -u toby:secret https://example.com/admin

# Or write the header yourself
curl -H "Authorization: Basic dG9ieTpzZWNyZXQ=" https://example.com/admin

# -v shows the 401 and WWW-Authenticate when no credentials are sent
curl -v https://example.com/admin

Using it in practice

nginx

The most common use: quickly lock down a staging site or an internal tool without touching application code:

# Create the credentials file (passwords are hashed, not stored in plain text)
htpasswd -c /etc/nginx/.htpasswd toby
location /admin {
    auth_basic           "Admin";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

The string after auth_basic is the realm sent in WWW-Authenticate.

Laravel

Laravel ships with the auth.basic middleware, which by default checks the email + password columns of the users table:

Route::get('/admin/report', ReportController::class)->middleware('auth.basic');

This middleware logs the user into the session after the first check. For a truly stateless setup (common for APIs), write your own middleware that calls Auth::onceBasic(): it authenticates only the current request and creates no session.

Security risks

  • HTTPS is mandatory. Base64 in the header is practically plain text. Over plain HTTP, anyone on the path (public Wi-Fi, proxies) can read the password.
  • The password goes out with every request. The real password crosses the network constantly, instead of a short-lived token. Leaking one request leaks the password.
  • There is no logout. The browser keeps the credentials until it is fully closed, and the server has no way to tell it to forget them.
  • No brute-force protection. You need to limit attempts, for example with nginx limit_req or fail2ban.
  • Prone to CSRF. The browser attaches the credentials to every request to the site, including requests triggered by other pages. Do not use Basic Auth for state-changing actions from the browser.

When to use it

Good fit Bad fit
Hiding staging or demo sites from strangers and bots Logging in end users
Internal tools with few users (dashboards, phpMyAdmin) Apps that need logout, roles, password reset
Server-to-server API calls over HTTPS Anything served over plain HTTP

Compared with the alternatives:

  • Session cookie: the password is sent once at login, then only a session id. Logout works and you get your own login form.
  • Bearer token / JWT: a token with an expiry replaces the password, and it can be revoked or expire without changing the password.

Basic Auth wins on simplicity: every browser, curl and HTTP library supports it out of the box, with zero client-side code.

View all