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"inWWW-Authenticatetells 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_reqorfail2ban. - 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.