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:

  1. 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: NTLM

    A 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.

  2. 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 200

    Inline 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.

  3. 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 200

    Keep temporary config files private and short-lived, then remove them after the authenticated task finishes.

  4. 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.

  5. 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
    200

    A 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.

  6. 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.