---
title: "RBAC"
url: "/reference/filters/http/rbac/"
kind: "reference"
summary: "HTTP filter that allows or denies requests by header-based policies, overridable per route."
---

> Arion site index: [/llms.txt](/llms.txt)

# RBAC

HTTP filter that allows or denies requests by header-based policies, overridable per route.

| Attachment | Type URL (`@type`) | Per-route override | Build feature |
|---|---|---|---|
| HTTP filter chain (`http_filters`) | `type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC` | Yes — `envoy.extensions.filters.http.rbac.v3.RBACPerRoute`, or `envoy.config.route.v3.FilterConfig` | None |

## What it does

HTTP RBAC evaluates named policies against each request and either lets it continue or answers `403` ("RBAC: access denied"). At L7 the facts available are request headers — including `:method`, `:path` and `:authority` pseudo-headers — matched with string matchers. For connection-level facts such as client IP or SNI, use the [network RBAC](@/reference/filters/network/rbac.md) filter; for expression-based policies over JWT claims, use [Cedar](@/reference/filters/http/cedar-policy.md).

## Minimal configuration

The fragment is an entry in `http_filters`, before the router:

```yaml,arion=fragment
- name: rbac
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC
    rules:
      action: DENY
      policies:
        block-legacy-clients:
          permissions:
            - any: true
          principals:
            - header:
                name: user-agent
                string_match:
                  prefix: "legacy-app/"
```

## Fields

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `rules.action` | enum | Yes | — | `ALLOW` (only matching requests pass) or `DENY` (matching requests get `403`). `LOG` is rejected. |
| `rules.policies` | map | Yes | — | Named policies, evaluated in lexical order of their names; the first whose permissions *and* principals both match decides. |

A policy's `permissions` and `principals` each accept two kinds:

| Kind | Value | Description |
|---|---|---|
| `any` | `true` | Always matches. |
| `header` | header matcher | `name` plus a `string_match` (`exact`, `prefix`, `suffix`, `contains`, `safe_regex`, each with optional `ignore_case`), `present_match`, `invert_match` or `treat_missing_header_as_empty`. |

Shadow rules, matchers, `rules_stat_prefix`, per-rule stats and policy `condition` expressions are unsupported and refused at startup.

## Per-route override

Routes adjust RBAC through `typed_per_filter_config`, keyed by the filter's name in the chain. An `RBACPerRoute` with `rbac` replaces the policies for that route; an empty `RBACPerRoute` removes RBAC from the route entirely; a `FilterConfig` with `disabled: true` switches the filter off:

```yaml,arion=fragment
routes:
  - match: { prefix: "/public" }
    route: { cluster: api }
    typed_per_filter_config:
      rbac:
        "@type": type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
        rbac: {}   # no rules: this route skips RBAC
```

The override must name an existing RBAC filter, or the configuration is rejected.

## Ordering and interactions

Place RBAC after [JWT authentication](@/reference/filters/http/jwt-authn.md) when policies match on headers the JWT filter writes via `claim_to_headers`. Denied requests short-circuit the chain before the router.
