Pulse 2025 Product Roundup: From Monitoring to AI-Native Control Plane

Read more

Elasticsearch HTTP 401 Unauthorized

An HTTP 401 from Elasticsearch means the cluster rejected your request at the authentication stage. The node could not verify who you are - either because no credentials were sent, the credentials were wrong, or the authentication token has expired. This is distinct from a 403, which means authentication succeeded but the authenticated identity lacks permission. A 401 always points to a credentials problem.

Why 401 Happens After Upgrading to Elasticsearch 8.x

Starting with Elasticsearch 8.0, security is enabled by default. Previous versions shipped with security disabled unless you explicitly configured xpack.security.enabled: true. In 8.x, a fresh installation auto-generates TLS certificates, creates the elastic superuser with a random password, and enables authentication on all HTTP and transport interfaces. Any client that previously connected without credentials will immediately get a 401.

The startup output prints the generated elastic password and an enrollment token. If you missed that output, reset the password with the bundled tool:

bin/elasticsearch-reset-password -u elastic

Clients also need to switch from http:// to https:// since TLS is on by default. A connection using the wrong scheme will either fail with a TLS handshake error or, if the port responds on plain HTTP for some reason, return 401 because the Authorization header never reached the security layer properly.

Missing or Malformed Authorization Header

The most common 401 trigger is a request with no Authorization header at all. Elasticsearch expects one of two formats: HTTP Basic authentication or an API key.

For basic auth, the header must be a Base64-encoded username:password string:

curl -u elastic:changeme https://localhost:9200
# Equivalent to:
curl -H "Authorization: Basic ZWxhc3RpYzpjaGFuZ2VtZQ==" https://localhost:9200

For API key auth, the header format uses the ApiKey prefix instead of Basic:

curl -H "Authorization: ApiKey VnVhQ2ZHY0JDZGJrUW..." https://localhost:9200

A subtle mistake is sending the API key's id or raw api_key value instead of the encoded field returned by the create API key response. The encoded field is a Base64 encoding of id:api_key - use it directly. Sending anything else produces a 401 with unable to authenticate user in the response body.

Expired or Invalidated API Keys

API keys can expire. When you create one with an expiration field, Elasticsearch will reject it with 401 once that timestamp passes. Check whether an API key is still valid:

GET /_security/api_key?id=VnVhQ2ZHY0JDZGJr

The response includes an expiration timestamp and an invalidated boolean. If invalidated is true, someone explicitly revoked the key through the invalidate API key endpoint. If expiration is in the past, the key has naturally expired. Either way, you need a new key:

POST /_security/api_key
{
  "name": "my-app-key",
  "expiration": "30d",
  "role_descriptors": {
    "my-app-role": {
      "cluster": ["monitor"],
      "indices": [
        {
          "names": ["app-data-*"],
          "privileges": ["read", "write"]
        }
      ]
    }
  }
}

The response contains id, api_key, and encoded. Store the encoded value - that is what goes into the Authorization: ApiKey header.

Anonymous Access and the 401/403 Boundary

Elasticsearch supports anonymous access through the xpack.security.authc.anonymous settings. When configured, requests without credentials are mapped to a virtual user with assigned roles instead of being immediately rejected. This changes the error behavior: an unauthenticated request that would normally produce 401 may instead produce 403 if anonymous access is enabled but the anonymous role lacks the required privileges.

The xpack.security.authc.anonymous.authz_exception setting controls this. When set to true (the default when anonymous access is enabled), authorization failures for the anonymous user return 403. When set to false, they return 401 instead, which prompts HTTP clients to show a credential dialog.

# elasticsearch.yml
xpack.security.authc.anonymous:
  username: anonymous_user
  roles: reader_role
  authz_exception: false  # Return 401 instead of 403 for anonymous failures

If you are debugging a system that sometimes returns 401 and sometimes 403 for what looks like the same unauthenticated request, check whether anonymous access is configured and which value authz_exception has.

Debugging Authentication Failures

When a 401 is not obvious, enable audit logging. Set xpack.security.audit.enabled: true in elasticsearch.yml and check the audit log for authentication_failed events. Each event includes the principal that was attempted, the realm chain that was consulted, and the reason for rejection.

# Search audit events for authentication failures
GET /.security-audit-*/_search
{
  "query": {
    "bool": {
      "must": [
        { "term": { "event.action": "authentication_failed" }},
        { "range": { "@timestamp": { "gte": "now-1h" }}}
      ]
    }
  }
}

The realm chain matters. Elasticsearch tries each configured realm in order - native, file, LDAP, SAML, PKI - and a 401 means all of them failed. A common misconfiguration is pointing a client at a native realm user while the cluster only has file realm users configured, or vice versa. The audit log will show exactly which realms were attempted and in what order.

For intermittent 401 errors on an otherwise working cluster, check for clock skew between clients and nodes. Token-based authentication and API key expiration checks rely on timestamps. If a node's clock drifts far enough, tokens that should still be valid get rejected. NTP synchronization across all nodes is a practical prerequisite for stable authentication.

Pulse - Elasticsearch Operations Done Right

Pulse can solve your Elasticsearch issues

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.