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

Read more

Elasticsearch HTTP 403 Forbidden

An HTTP 403 from Elasticsearch means authentication succeeded but authorization failed. The cluster knows who you are - it just determined that your user does not have the required privileges for the requested action. Every 403 response includes a message describing what privilege was missing, making it more debuggable than a 401 if you know where to look.

Reading the 403 Error Message

Elasticsearch 403 responses follow a predictable structure. The error body tells you exactly which action was denied:

{
  "error": {
    "type": "security_exception",
    "reason": "action [indices:data/write/bulk] is unauthorized for user [app-writer] with effective roles [read-only-role], this action is granted by the index privileges [create_doc,create,delete,index,write,all]"
  },
  "status": 403
}

This message gives you everything: the action that was attempted (indices:data/write/bulk), the user (app-writer), the roles in effect (read-only-role), and the list of index privileges that would grant the missing permission. When you see a 403, read the full error body before doing anything else. Most fixes are a direct mapping from the denied action to the privilege listed in the error.

Diagnosing with the has_privileges API

Before modifying roles, confirm what the user can and cannot do. The _has_privileges API lets you probe specific privileges without making actual requests that might fail:

GET /_security/user/_has_privileges
{
  "cluster": ["manage_index_templates", "monitor"],
  "index": [
    {
      "names": ["logs-*"],
      "privileges": ["read", "write", "create_index"]
    }
  ]
}

The response returns a boolean for each privilege:

{
  "has_all_requested": false,
  "cluster": {
    "manage_index_templates": false,
    "monitor": true
  },
  "index": {
    "logs-*": {
      "read": true,
      "write": false,
      "create_index": false
    }
  }
}

By default this API checks the calling user's own privileges. To check another user, run the request with the es-security-runas-user header set to the target username - but the calling user must have the run_as privilege for that to work.

Common Index Privilege Gaps

Most 403 errors come from index-level privilege mismatches. A role might grant read on an index pattern but the application also needs write, create_index, or view_index_metadata. Here are the actions that catch people off guard:

The auto_configure privilege is needed when data streams or composable index templates create new backing indices. Without it, a user can write to an existing index but gets 403 when the data stream rolls over. ILM rollover and index lifecycle operations require manage_ilm at the cluster level and manage on the target indices.

The maintenance privilege (added in 8.x) allows flush, refresh, and synced-flush operations without granting the broader manage privilege. Operations teams that previously used manage for routine index maintenance can now scope this down.

A corrected role definition for a typical application writing to data streams:

PUT /_security/role/app-writer
{
  "cluster": ["monitor"],
  "indices": [
    {
      "names": ["logs-app-*"],
      "privileges": ["write", "create_index", "auto_configure", "view_index_metadata"]
    }
  ]
}

Cluster Privilege Gaps and Hidden Actions

Some operations require cluster-level privileges that are not obvious from the API endpoint. Creating or updating index templates requires manage_index_templates. Executing enrich policies requires manage_enrich. Checking cluster health requires monitor. The full list of cluster privileges is in the Elasticsearch security privileges documentation, but the pattern is consistent: administrative operations on cluster-wide resources need explicit cluster privileges, and the 403 error message will name the action that was denied.

A less obvious case is cross-cluster search and replication. Remote cluster connections require the manage cluster privilege on the local cluster to configure, and the remote cluster needs roles that grant the appropriate index privileges for the indices being searched or replicated. A 403 during cross-cluster search usually means the API key or user credential used for the remote connection does not have read on the target remote indices.

Document-Level and Field-Level Security

Document-level security (DLS) and field-level security (FLS) do not produce 403 errors - they silently filter results. A user with DLS restrictions sees a subset of documents; a user with FLS restrictions sees a subset of fields. This is by design, but it confuses debugging when a query returns empty results or missing fields and the developer assumes a permissions error.

PUT /_security/role/team-a-reader
{
  "indices": [
    {
      "names": ["shared-index"],
      "privileges": ["read"],
      "query": "{\"term\": {\"team\": \"a\"}}",
      "field_security": {
        "grant": ["title", "content", "team", "timestamp"]
      }
    }
  ]
}

If team-a-reader queries shared-index and gets zero hits, the first instinct is to check permissions. But the user has read access - the DLS query just filtered out everything because the team field does not match. Check the role's query and field_security definitions before escalating privileges.

One practical trap: if a DLS query references a field that FLS hides, the DLS filter breaks silently. The filtered field evaluates as missing, which typically excludes all documents. When combining DLS and FLS on the same role, every field used in the DLS query must be included in the FLS grant list.

Superuser vs Scoped Roles

The elastic superuser and the built-in superuser role bypass all authorization checks. If a request works with elastic credentials but fails with 403 for a custom user, the problem is always in the custom role definition. Resist the temptation to leave applications running as elastic in production - that user has unrestricted access including the ability to read .security internal indices and impersonate other users.

Build scoped roles with the minimum privileges the application needs. Use _has_privileges to validate them before deploying. The kibana_admin and editor built-in roles are useful starting points for Kibana users, but for backend services interacting with Elasticsearch directly, custom roles with explicit index patterns and privilege sets are the right approach. When a 403 appears, the fix is almost always adding the specific missing privilege to the role, not replacing the role with something broader.

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.