cURL Basic Auth: How to Send Authenticated Requests from the Command Line
By Nicholas St. Germain —
cURL Basic Auth (TL;DR)
The fastest way to send a Basic Auth request with cURL is the -u flag:
curl -u username:password https://api.example.com/me
cURL takes care of Base64-encoding your credentials and attaching the Authorization: Basic ... header. The rest of this guide covers the manual header approach, .netrc files, URL-embedded credentials, common 401 errors, and how to layer Basic Auth on top of an authenticated proxy.
What Is HTTP Basic Authentication?
HTTP Basic Authentication is one of the oldest and simplest auth schemes on the web. The client sends a username and password - concatenated with a colon and Base64-encoded - in the Authorization header on every request:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
That string decodes back to username:password. Base64 is not encryption - it's encoding. Always use HTTPS so the header isn't readable on the wire.
Despite being decades old, Basic Auth is still everywhere: internal admin panels, legacy APIs, Docker registries, Jenkins, Elasticsearch, Grafana, lots of self-hosted services, and most proxy servers.
Sending Basic Auth with cURL
The -u / --user flag
The most common way:
curl -u username:password https://api.example.com/data
If you leave off the password, cURL prompts for it interactively - useful when you don't want the password in your shell history:
curl -u username https://api.example.com/data
Enter host password for user 'username':
Verifying the header was sent
Add -v to print the request headers cURL is sending:
curl -v -u alice:s3cret https://api.example.com/me
You should see a line like:
> Authorization: Basic YWxpY2U6czNjcmV0
If that line is missing, cURL didn't apply the credentials - usually because of a typo in the flag.
Setting the Authorization header manually
You can also build the header yourself with -H. This is handy when you're scripting and already have a Base64 token sitting in a variable:
TOKEN=$(printf 'username:password' | base64)
curl -H "Authorization: Basic $TOKEN" https://api.example.com/data
The result is identical to using -u, but you control exactly what goes on the wire.
Credentials in the URL
cURL also accepts https://user:pass@host/path syntax:
curl https://username:password@api.example.com/data
It works, but it's the worst of the options for two reasons:
- The full URL - including the password - gets logged by shells, proxies, and many web servers.
- Special characters in the password (
@,:,/,#) need to be percent-encoded.
Reach for it for a quick test, not for anything that lands in a script.
Using a .netrc File
For repeated calls to the same host, drop the credentials into ~/.netrc and let cURL pick them up automatically:
machine api.example.com
login username
password s3cret
Lock down the permissions (cURL refuses to read it otherwise):
chmod 600 ~/.netrc
Then:
curl --netrc https://api.example.com/data
Or point at a specific file with --netrc-file ./my-creds. This keeps secrets out of your shell history and out of any scripts you commit.
Handling Special Characters
If your password contains !, $, backticks, or quotes, the shell will eat them before cURL sees them. Wrap the whole -u argument in single quotes:
curl -u 'username:p@ss!w0rd$' https://api.example.com/data
For values with single quotes, use the variable trick:
PASS='weird'\''quote'
curl -u "username:$PASS" https://api.example.com/data
When credentials live in the URL, percent-encode special characters: @ becomes %40, : becomes %3A, / becomes %2F.
Common Errors
- 401 Unauthorized - credentials missing, wrong, or not in the Basic Auth scheme. Re-run with
-vand confirm theAuthorizationheader is on the request. - 403 Forbidden - credentials are valid but the user isn't allowed to access the resource. This is an authorization (not authentication) problem.
- WWW-Authenticate: Bearer ... in the response - the server expects a token, not Basic Auth. Switch to
-H "Authorization: Bearer <token>". - Connection closes immediately on HTTP - many APIs reject Basic Auth over plain HTTP. Use
https://.
Basic Auth Through an Authenticated Proxy
Things get interesting when both your proxy and your target API require credentials. cURL keeps them separate:
-x/--proxy- the proxy URL (with credentials embedded or via-U).-u/--user- the target's credentials.
Example: hitting a Basic-Auth-protected API through a Stat Proxies static IP:
curl -x http://proxyuser:proxypass@212.116.248.54:3128 \
-u apiuser:apipass \
https://api.example.com/data
cURL sends a Proxy-Authorization header to the proxy and an Authorization header to the upstream server. They never collide.
If you'd rather keep the proxy creds out of the URL, use -U:
curl -x http://212.116.248.54:3128 \
-U proxyuser:proxypass \
-u apiuser:apipass \
https://api.example.com/data
This is the pattern we recommend when you're scraping or automating against an API that gates access by both IP allowlist and credentials.
Storing Credentials Safely
A few rules of thumb:
- Never hard-code passwords in scripts you commit to git.
- Use
.netrc(withchmod 600) or environment variables loaded from a.envfile that's in.gitignore. - Pull from a secrets manager (1Password CLI,
aws secretsmanager, Vault) for production:
PASS=$(op read "op://Private/api.example.com/password")
curl -u "apiuser:$PASS" https://api.example.com/data
- Rotate credentials when teammates leave and after any
-vdebugging session that logs them.
Conclusion
Basic Auth in cURL boils down to one flag - -u username:password - but the surrounding details (special characters, .netrc, manual headers, proxy stacking) are what trip people up in the real world. Bookmark the snippets above and you'll be able to authenticate against any Basic-Auth-protected endpoint in a single command.
If you also need to route those authenticated requests through a stable, allowlisted IP - for scraping, regional testing, or just keeping your origin off vendor logs - take a look at the static IP plans from Stat Proxies. For a broader walkthrough of cURL's proxy flags, see our guide to using cURL with proxies.