---
title: "Rate limiting"
url: "/docs/traffic/rate-limiting/"
kind: "feature"
summary: "Choose the right limiter: listener accept limits, connection caps, an external rate limit service, per-route token buckets, or per-user limits."
---

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

# Rate limiting

Choose the right limiter: listener accept limits, connection caps, an external rate limit service, per-route token buckets, or per-user limits.

Arion has five rate limiters, each guarding a different layer. Pick by what you need to protect: the accept loop, concurrent connections, a shared quota across proxies, request throughput, or individual users.

| Limiter | Attaches to | Limits | On limit |
| --- | --- | --- | --- |
| [Listener local](@/reference/filters/listener/local-rate-limit.md) | Listener | New connections per interval | Connection dropped |
| [Connection limit](@/reference/filters/network/connection-limit.md) | Filter chain | Concurrent connections | Closed, optionally after a delay |
| [Network global](@/reference/filters/network/rate-limit.md) | Filter chain | New connections, via external RLS | Connection dropped |
| [HTTP local](@/reference/filters/http/local-rate-limit.md) | HTTP filter / route | Requests per interval | HTTP 429 |
| [User](@/reference/filters/http/user-rate-limit.md) | HTTP filter | Requests per user | HTTP 429 |

All local limiters share the same token-bucket shape: `max_tokens` is the burst size, and `tokens_per_fill` tokens return every `fill_interval`.

## Listener local rate limit

A listener filter that drops connections above the bucket rate before any filter chain work happens — the cheapest place to shed an accept flood:

```yaml,arion=fragment
listener_filters:
  - name: envoy.filters.listener.local_ratelimit
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.listener.local_ratelimit.v3.LocalRateLimit
      stat_prefix: listener_rl
      token_bucket: { max_tokens: 100, tokens_per_fill: 100, fill_interval: 1s }
```

## Connection limit

A network filter that caps concurrent connections on a filter chain with one process-wide counter. `delay` holds excess connections open before closing them, which slows down flooders:

```yaml,arion=fragment
filters:
  - name: envoy.filters.network.connection_limit
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.network.connection_limit.v3.ConnectionLimit
      stat_prefix: cx_limit
      max_connections: 1000
      delay: 0.5s
```

## Network global rate limit

A network filter that asks an external rate limit service (gRPC) about every new connection, so one quota spans a whole fleet. When the RLS response carries a quota, Arion caches it for the quota window and answers locally until it is spent:

```yaml,arion=fragment
filters:
  - name: envoy.filters.network.ratelimit
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.network.ratelimit.v3.RateLimit
      stat_prefix: global_rl
      domain: api.example.com
      failure_mode_deny: true
      descriptors:
        - entries:
            - { key: destination_cluster, value: api }
      rate_limit_service:
        grpc_service:
          envoy_grpc: { cluster_name: rls }
```

On listeners with a [TLS inspector](@/reference/filters/listener/tls-inspector.md), `domain` may be omitted: the connection's SNI becomes the RLS domain, giving each tenant its own quota namespace with no per-tenant config. Without the inspector, `domain` is required and the config is refused otherwise.

## HTTP local rate limit

An HTTP filter with a token bucket per filter instance, returning 429 (configurable via `status`) above the rate. A route can override the bucket through `typed_per_filter_config`, so different routes get different budgets:

```yaml,arion=fragment
http_filters:
  - name: envoy.filters.http.local_ratelimit
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
      stat_prefix: http_rl
      token_bucket: { max_tokens: 50, tokens_per_fill: 50, fill_interval: 1s }
```

## User rate limit

An Arion-native HTTP filter that keys buckets on a request header identifying the caller. Entries with a `user_id` pin a limit to that user; the entry without one is the default for any other identified caller, and requests missing the header pass through unlimited. `simple_rate_limit` is shorthand for a bucket of `max_tokens` refilling at `rate` tokens per second:

```yaml,arion=fragment
http_filters:
  - name: arion.filters.http.user_rate_limit
    typed_config:
      "@type": type.googleapis.com/arion.extensions.filters.http.user_rate_limit.v3.UserRateLimiter
      user_id_header_name: x-api-key
      user_rate_limits:
        - user_id: "premium-key"
          simple_rate_limit: { max_tokens: 100, rate: 100 }
        - simple_rate_limit: { max_tokens: 5, rate: 5 }
```

## Verify

With a small bucket (`max_tokens: 2`), exhaust it and watch the status flip:

```bash
for i in 1 2 3; do curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/; done
```

```text
200
200
429
```

## Limits

- Local buckets live in one Arion process; use the network global filter when a quota must span replicas.
- Global rate limiting is network-level only; the HTTP global rate limit filter is not supported.
- RLS descriptors are static key/value pairs; dynamic entries from request attributes are not supported.
- The RLS cluster must speak HTTP/2 (gRPC).
- The user rate limiter only limits requests that carry the user-id header; the default entry applies to identified users without their own entry.

Each limiter's full field table is on its reference page, linked above; the [connection limit reference](@/reference/filters/network/connection-limit.md) covers the counter semantics.
