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:

  1. The full URL - including the password - gets logged by shells, proxies, and many web servers.
  2. 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 -v and confirm the Authorization header 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 (with chmod 600) or environment variables loaded from a .env file 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 -v debugging 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.