NTLM still appears on IIS applications, Windows-integrated intranet tools, and legacy proxy paths that protect internal APIs or reports. When a script, smoke test, or incident check needs the same access without a browser session, cURL can drive the HTTP authentication exchange directly from the terminal.
NTLM is a challenge-response method rather than a single static header. A target server usually starts with 401 Unauthorized and WWW-Authenticate: NTLM, while an authenticating proxy uses 407 Proxy Authentication Required and Proxy-Authenticate: NTLM. In cURL, --ntlm with --user handles the origin-server case, and --proxy-ntlm with --proxy-user handles the proxy case.
NTLM is best treated as a compatibility path rather than a default authentication choice. Inline passwords can leak through shell history and process listings, and a reused command is safer when the credentials move into a temporary config file or a dedicated credential store. The steps below stay focused on explicit NTLM over HTTPS and use a masked domain-qualified service account, a redacted secret placeholder, and direct verification so repeated 401 or 407 responses are easy to separate from unrelated network failures.
Steps to authenticate with NTLM using cURL:
- Probe the endpoint without credentials so the NTLM challenge and the authentication layer are visible before sending a username or password.
$ curl --disable --silent --show-error --verbose \ --output /dev/null \ https://reports-api.example.test/reports/weekly-summary 2>&1 | grep -E '^(< HTTP/|< WWW-Authenticate:|< Proxy-Authenticate:)' < HTTP/2 401 < WWW-Authenticate: NTLMA 401 with WWW-Authenticate: NTLM means the origin server expects NTLM, while a 407 with Proxy-Authenticate: NTLM means the proxy step later in this page is the correct path.
- Send the request with --ntlm and the account used by the protected service.
$ curl --silent --show-error --fail \ --ntlm \ --user 'OPS\svc-report-readonly:REDACTED_NTLM_PASSWORD' \ --write-out '\nHTTP %{http_code}\n' \ https://reports-api.example.test/reports/weekly-summary { "report": "weekly-summary", "status": "ok", "authenticated_as": "OPS\\svc-report-readonly" } HTTP 200Inline credentials are visible in shell history and process listings, so treat this pattern as a quick local test rather than a long-lived automation command.
If the server advertises multiple schemes and explicit NTLM is not required, --anyauth with --user lets cURL probe first and choose a supported method automatically at the cost of one extra round-trip.
- Move repeated NTLM requests into a dedicated cURL config file so the secret does not remain on the literal command line.
$ cat <<'EOF' > curl-ntlm.conf url = "https://reports-api.example.test/reports/weekly-summary" ntlm user = "OPS\\svc-report-readonly:REDACTED_NTLM_PASSWORD" EOF $ chmod 600 curl-ntlm.conf $ curl --silent --show-error --fail --config curl-ntlm.conf --write-out '\nHTTP %{http_code}\n' { "report": "weekly-summary", "status": "ok", "authenticated_as": "OPS\\svc-report-readonly" } HTTP 200Keep temporary config files private and short-lived, then remove them after the authenticated task finishes.
- Use proxy-specific NTLM options when the challenge comes from the proxy instead of the destination server.
$ curl --silent --show-error --fail \ --proxy http://proxy.example.test:3128 \ --proxy-ntlm \ --proxy-user 'OPS\svc-report-readonly:REDACTED_NTLM_PASSWORD' \ --write-out '\nHTTP %{http_code}\n' \ https://reports-api.example.test/reports/weekly-summary { "report": "weekly-summary", "status": "ok", "authenticated_as": "OPS\\svc-report-readonly" } HTTP 200--proxy-ntlm and --proxy-user only apply to proxy authentication. Keep --ntlm and --user for origin-server challenges.
If the proxy can negotiate several schemes and NTLM is only one option, --proxy-anyauth can probe first and choose a supported method automatically.
- Check only the final status code when a script or health check must prove the NTLM request succeeded without printing protected content.
$ curl --silent --show-error \ --ntlm \ --user 'OPS\svc-report-readonly:REDACTED_NTLM_PASSWORD' \ --output /dev/null \ --write-out '%{http_code}\n' \ https://reports-api.example.test/reports/weekly-summary 200A final 200 or application-specific success code confirms the handshake completed; repeated 401 or 407 responses usually mean the wrong credential pair, the wrong auth layer, or a server-side policy mismatch.
- Capture a verbose log when the challenge never clears so the header exchange and status changes can be compared with server or proxy logs.
$ curl --disable --silent --show-error --verbose \ --ntlm \ --user 'OPS\svc-report-readonly:REDACTED_NTLM_PASSWORD' \ --output /dev/null \ https://reports-api.example.test/reports/weekly-summary 2> ntlm-debug.log $ grep -E '^(> |< HTTP/|< WWW-Authenticate:|< Proxy-Authenticate:|\*)' ntlm-debug.log * Host reports-api.example.test:443 was resolved. ##### snipped ##### > GET /reports/weekly-summary HTTP/2 ##### snipped ##### < HTTP/2 401 < WWW-Authenticate: NTLM ##### snipped #####Verbose logs can expose credentials, cookies, and request metadata, so keep them local and redact them before sharing.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
