# limit-count

The `limit-count` plugin uses fixed or sliding windows to limit the number of requests associated with a key during a configured time interval. Requests exceeding the quota are rejected, and responses include quota headers by default.

## Local vs Redis Rate Limiting[​](#local-vs-redis-rate-limiting "Direct link to Local vs Redis Rate Limiting")

The `limit-count` plugin supports two modes of rate limiting:

* **Local rate limiting**: Limits are enforced independently on each gateway instance. Each instance maintains its own counters, so the effective limit is roughly (limit × number of instances) when traffic is spread across instances. This is the default when no `policy` is set or when `policy` is `local`.
* **Redis-based rate limiting**: Limits are shared across all gateway instances through Redis. All instances share the same quota, so the configured limit applies to all gateway instances. Set `policy` to `redis` for a single Redis instance, `redis-cluster` for a Redis cluster, or `redis-sentinel` for Redis nodes managed by Redis Sentinel.

API7 Enterprise retains the separate [`limit-count-advanced`](https://docs.api7.ai/hub/limit-count-advanced.md) plugin for backward compatibility. See [Configuration](https://docs.api7.ai/hub/limit-count/configuration.md) for field availability and behavior.

## Examples[​](#examples "Direct link to Examples")

The examples below demonstrate how you can configure `limit-count` in different scenarios.

### Apply Rate Limiting by Remote Address[​](#apply-rate-limiting-by-remote-address "Direct link to Apply Rate Limiting by Remote Address")

The following example demonstrates the rate limiting of requests by a single variable, `remote_addr`.

Create a route with `limit-count` plugin that allows for a quota of 1 within a 30-second window per remote address:

* Admin API
* ADC
* Ingress Controller

```
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "limit-count-route",

    "uri": "/get",

    "plugins": {

      "limit-count": {

        "count": 1,

        "time_window": 30,

        "rejected_code": 429,

        "key_type": "var",

        "key": "remote_addr",

        "policy": "local"

      }

    },

    "upstream": {

      "type": "roundrobin",

      "nodes": {

        "httpbin.org:80": 1

      }

    }

  }'
```

adc.yaml

```
services:

  - name: httpbin

    routes:

      - uris:

          - /get

        name: limit-count-route

        plugins:

          limit-count:

            count: 1

            time_window: 30

            rejected_code: 429

            key_type: var

            key: remote_addr

            policy: local

    upstream:

      type: roundrobin

      nodes:

        - host: httpbin.org

          port: 80

          weight: 1
```

Preview the changes and confirm that they only affect resources for this guide:

```
adc diff -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

Synchronize the configuration to the gateway:

```
adc sync -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

* Gateway API
* APISIX CRD

limit-count-ic.yaml

```
apiVersion: v1

kind: Service

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  type: ExternalName

  externalName: httpbin.org

---

apiVersion: apisix.apache.org/v1alpha1

kind: PluginConfig

metadata:

  namespace: aic

  name: limit-count-plugin-config

spec:

  plugins:

    - name: limit-count

      config:

        count: 1

        time_window: 30

        rejected_code: 429

        key_type: var

        key: remote_addr

        policy: local

---

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  namespace: aic

  name: limit-count-route

spec:

  parentRefs:

    - name: apisix

  rules:

    - matches:

        - path:

            type: Exact

            value: /get

      filters:

        - type: ExtensionRef

          extensionRef:

            group: apisix.apache.org

            kind: PluginConfig

            name: limit-count-plugin-config

      backendRefs:

        - name: httpbin-external-domain

          port: 80
```

limit-count-ic.yaml

```
apiVersion: apisix.apache.org/v2

kind: ApisixUpstream

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  ingressClassName: apisix

  externalNodes:

  - type: Domain

    name: httpbin.org

---

apiVersion: apisix.apache.org/v2

kind: ApisixRoute

metadata:

  namespace: aic

  name: limit-count-route

spec:

  ingressClassName: apisix

  http:

    - name: limit-count-route

      match:

        paths:

          - /get

      upstreams:

      - name: httpbin-external-domain

      plugins:

      - name: limit-count

        enable: true

        config:

          count: 1

          time_window: 30

          rejected_code: 429

          key_type: var

          key: remote_addr

          policy: local
```

Apply the configuration to your cluster:

```
kubectl apply -f limit-count-ic.yaml
```

Send a request to verify:

```
curl -i "http://127.0.0.1:9080/get"
```

You should see an `HTTP/1.1 200 OK` response.

The request has consumed all the quota allowed for the time window. If you send the request again within the same 30-second time interval, you should receive an `HTTP/1.1 429 Too Many Requests` response, indicating the request surpasses the quota threshold.

### Apply Rate Limiting with a Sliding Window[​](#apply-rate-limiting-with-a-sliding-window "Direct link to Apply Rate Limiting with a Sliding Window")

The following example demonstrates how to use a sliding window to smooth traffic bursts at window boundaries by weighting the previous window's count. Sliding windows were introduced in API7 Enterprise 3.9.14 and 3.10.1, and APISIX 3.18.0.

Create a route with the `limit-count` plugin that allows a quota of 10 requests within a 30-second sliding window per remote address:

* Admin API
* ADC
* Ingress Controller

```
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "limit-count-route",

    "uri": "/get",

    "plugins": {

      "limit-count": {

        "count": 10,

        "time_window": 30,

        "rejected_code": 429,

        "key_type": "var",

        "key": "remote_addr",

        "policy": "local",

        "window_type": "sliding"

      }

    },

    "upstream": {

      "type": "roundrobin",

      "nodes": {

        "httpbin.org:80": 1

      }

    }

  }'
```

adc.yaml

```
services:

  - name: httpbin

    routes:

      - name: limit-count-route

        uris:

          - /get

        plugins:

          limit-count:

            count: 10

            time_window: 30

            rejected_code: 429

            key_type: var

            key: remote_addr

            policy: local

            window_type: sliding

    upstream:

      type: roundrobin

      nodes:

        - host: httpbin.org

          port: 80

          weight: 1
```

Preview the changes and confirm that they only affect resources for this guide:

```
adc diff -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

Synchronize the configuration to the gateway:

```
adc sync -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

* Gateway API
* APISIX CRD

limit-count-ic.yaml

```
apiVersion: v1

kind: Service

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  type: ExternalName

  externalName: httpbin.org

---

apiVersion: apisix.apache.org/v1alpha1

kind: PluginConfig

metadata:

  namespace: aic

  name: limit-count-plugin-config

spec:

  plugins:

    - name: limit-count

      config:

        count: 10

        time_window: 30

        rejected_code: 429

        key_type: var

        key: remote_addr

        policy: local

        window_type: sliding

---

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  namespace: aic

  name: limit-count-route

spec:

  parentRefs:

    - name: apisix

  rules:

    - matches:

        - path:

            type: Exact

            value: /get

      filters:

        - type: ExtensionRef

          extensionRef:

            group: apisix.apache.org

            kind: PluginConfig

            name: limit-count-plugin-config

      backendRefs:

        - name: httpbin-external-domain

          port: 80
```

limit-count-ic.yaml

```
apiVersion: apisix.apache.org/v2

kind: ApisixUpstream

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  ingressClassName: apisix

  externalNodes:

    - type: Domain

      name: httpbin.org

---

apiVersion: apisix.apache.org/v2

kind: ApisixRoute

metadata:

  namespace: aic

  name: limit-count-route

spec:

  ingressClassName: apisix

  http:

    - name: limit-count-route

      match:

        paths:

          - /get

      upstreams:

        - name: httpbin-external-domain

      plugins:

        - name: limit-count

          enable: true

          config:

            count: 10

            time_window: 30

            rejected_code: 429

            key_type: var

            key: remote_addr

            policy: local

            window_type: sliding
```

Apply the configuration to your cluster:

```
kubectl apply -f limit-count-ic.yaml
```

Send a request to verify:

```
curl -i "http://127.0.0.1:9080/get"
```

You should see an `HTTP/1.1 200 OK` response. As you keep sending requests, the sliding window enforces the quota more evenly across the boundary between consecutive windows than the fixed window does.

### Apply Rate Limiting by Remote Address and Consumer Name[​](#apply-rate-limiting-by-remote-address-and-consumer-name "Direct link to Apply Rate Limiting by Remote Address and Consumer Name")

The following example demonstrates the rate limiting of requests by a combination of variables, `remote_addr` and `consumer_name`. It allows for a quota of 1 within a 30-second window per remote address and for each [consumer](https://docs.api7.ai/apisix/key-concepts/consumers.md).

* Admin API
* ADC
* Ingress Controller

Create consumer `john`:

```
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "username": "john"

  }'
```

Create `key-auth` credential for consumer `john`:

```
curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "cred-john-key-auth",

    "plugins": {

      "key-auth": {

        "key": "john-key"

      }

    }

  }'
```

Create consumer `jane`:

```
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "username": "jane"

  }'
```

Create `key-auth` credential for consumer `jane`:

```
curl "http://127.0.0.1:9180/apisix/admin/consumers/jane/credentials" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "cred-jane-key-auth",

    "plugins": {

      "key-auth": {

        "key": "jane-key"

      }

    }

  }'
```

Create a route with `key-auth` and `limit-count` plugins:

```
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "limit-count-route",

    "uri": "/get",

    "plugins": {

      "key-auth": {},

      "limit-count": {

        "count": 1,

        "time_window": 30,

        "rejected_code": 429,

        "key_type": "var_combination",

        "key": "$remote_addr $consumer_name",

        "policy": "local"

      }

    },

    "upstream": {

      "type": "roundrobin",

      "nodes": {

        "httpbin.org:80": 1

      }

    }

  }'
```

Create two consumers and a route that enables rate limiting by consumers:

adc.yaml

```
consumers:

  - username: john

    credentials:

      - name: key-auth

        type: key-auth

        config:

          key: john-key

  - username: jane

    credentials:

      - name: key-auth

        type: key-auth

        config:

          key: jane-key

services:

  - name: limit-count-service

    routes:

      - name: limit-count-route

        uris:

          - /get

        plugins:

          key-auth: {}

          limit-count:

            count: 1

            time_window: 30

            rejected_code: 429

            key_type: var_combination

            key: "$remote_addr $consumer_name"

            policy: local

    upstream:

      type: roundrobin

      nodes:

        - host: httpbin.org

          port: 80

          weight: 1
```

Preview the changes and confirm that they only affect resources for this guide:

```
adc diff -f adc.yaml \

  --include-resource-type consumer \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

Synchronize the configuration to the gateway:

```
adc sync -f adc.yaml \

  --include-resource-type consumer \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

Create two consumers and a route that enables rate limiting by consumers:

* Gateway API
* APISIX CRD

limit-count-ic.yaml

```
apiVersion: apisix.apache.org/v1alpha1

kind: Consumer

metadata:

  namespace: aic

  name: john

spec:

  gatewayRef:

    name: apisix

  credentials:

    - type: key-auth

      name: primary-key

      config:

        key: john-key

---

apiVersion: apisix.apache.org/v1alpha1

kind: Consumer

metadata:

  namespace: aic

  name: jane

spec:

  gatewayRef:

    name: apisix

  credentials:

    - type: key-auth

      name: primary-key

      config:

        key: jane-key

---

apiVersion: v1

kind: Service

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  type: ExternalName

  externalName: httpbin.org

---

apiVersion: apisix.apache.org/v1alpha1

kind: PluginConfig

metadata:

  namespace: aic

  name: limit-count-plugin-config

spec:

  plugins:

    - name: key-auth

      config:

        _meta:

          disable: false

    - name: limit-count

      config:

        count: 1

        time_window: 30

        rejected_code: 429

        key_type: var_combination

        key: "$remote_addr $consumer_name"

        policy: local

---

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  namespace: aic

  name: limit-count-route

spec:

  parentRefs:

    - name: apisix

  rules:

    - matches:

        - path:

            type: Exact

            value: /get

      filters:

        - type: ExtensionRef

          extensionRef:

            group: apisix.apache.org

            kind: PluginConfig

            name: limit-count-plugin-config

      backendRefs:

        - name: httpbin-external-domain

          port: 80
```

limit-count-ic.yaml

```
apiVersion: apisix.apache.org/v2

kind: ApisixConsumer

metadata:

  namespace: aic

  name: john

spec:

  ingressClassName: apisix

  authParameter:

    keyAuth:

      value:

        key: john-key

---

apiVersion: apisix.apache.org/v2

kind: ApisixConsumer

metadata:

  namespace: aic

  name: jane

spec:

  ingressClassName: apisix

  authParameter:

    keyAuth:

      value:

        key: jane-key

---

apiVersion: apisix.apache.org/v2

kind: ApisixUpstream

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  ingressClassName: apisix

  externalNodes:

  - type: Domain

    name: httpbin.org

---

apiVersion: apisix.apache.org/v2

kind: ApisixRoute

metadata:

  namespace: aic

  name: limit-count-route

spec:

  ingressClassName: apisix

  http:

    - name: limit-count-route

      match:

        paths:

          - /get

      upstreams:

      - name: httpbin-external-domain

      plugins:

      - name: key-auth

        enable: true

      - name: limit-count

        enable: true

        config:

          count: 1

          time_window: 30

          rejected_code: 429

          key_type: var_combination

          key: "$remote_addr $consumer_name"

          policy: local
```

Apply the configuration to your cluster:

```
kubectl apply -f limit-count-ic.yaml
```

❶ `key-auth`: enable key authentication on the route.

❷ `key_type`: set to `var_combination` to interpret the `key` as a combination of variables.

❸ `key`: set to `$remote_addr $consumer_name` to apply rate limiting quota by remote address and for each consumer.

Send a request as the consumer `jane`:

```
curl -i "http://127.0.0.1:9080/get" -H 'apikey: jane-key'
```

You should see an `HTTP/1.1 200 OK` response with the corresponding response body.

This request has consumed all the quota set for the time window. If you send the same request as the consumer `jane` within the same 30-second time interval, you should receive an `HTTP/1.1 429 Too Many Requests` response, indicating the request surpasses the quota threshold.

Send the same request as the consumer `john` within the same 30-second time interval:

```
curl -i "http://127.0.0.1:9080/get" -H 'apikey: john-key'
```

You should see an `HTTP/1.1 200 OK` response with the corresponding response body, indicating the request is not rate limited.

Send the same request as the consumer `john` again within the same 30-second time interval, you should receive an `HTTP/1.1 429 Too Many Requests` response.

This verifies the plugin rate limits by the combination of variables, `remote_addr` and `consumer_name`.

### Share Quota among Routes[​](#share-quota-among-routes "Direct link to Share Quota among Routes")

The following example demonstrates the sharing of rate limiting quota among multiple routes by configuring the `group` of the `limit-count` plugin.

Note that the configurations of the `limit-count` plugin of the same `group` should be identical. To avoid update anomalies and repetitive configurations, you can create a [service](https://docs.api7.ai/apisix/key-concepts/services.md) with `limit-count` plugin and upstream for routes to connect to.

* Admin API
* ADC
* Ingress Controller

Create a service with rate limiting group:

```
curl "http://127.0.0.1:9180/apisix/admin/services" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "limit-count-service",

    "plugins": {

      "limit-count": {

        "count": 1,

        "time_window": 30,

        "rejected_code": 429,

        "policy": "local",

        "group": "srv1"

      }

    },

    "upstream": {

      "type": "roundrobin",

      "nodes": {

        "httpbin.org:80": 1

      }

    }

  }'
```

Create two routes that use the same service to share the quota:

```
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "limit-count-route-1",

    "service_id": "limit-count-service",

    "uri": "/get1",

    "plugins": {

      "proxy-rewrite": {

        "uri": "/get"

      }

    }

  }'
```

```
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "limit-count-route-2",

    "service_id": "limit-count-service",

    "uri": "/get2",

    "plugins": {

      "proxy-rewrite": {

        "uri": "/get"

      }

    }

  }'
```

Create a service with two routes that share the same rate limiting quota:

adc.yaml

```
services:

  - name: limit-count-service

    plugins:

      limit-count:

        count: 1

        time_window: 30

        rejected_code: 429

        policy: local

        group: srv1

    routes:

      - name: limit-count-route-1

        uris:

          - /get1

        plugins:

          proxy-rewrite:

            uri: /get

      - name: limit-count-route-2

        uris:

          - /get2

        plugins:

          proxy-rewrite:

            uri: /get

    upstream:

      type: roundrobin

      nodes:

        - host: httpbin.org

          port: 80

          weight: 1
```

Preview the changes and confirm that they only affect resources for this guide:

```
adc diff -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

Synchronize the configuration to the gateway:

```
adc sync -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

* Gateway API
* APISIX CRD

Create two HTTPRoute resources that reference the same PluginConfig to share quota:

limit-count-ic.yaml

```
apiVersion: v1

kind: Service

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  type: ExternalName

  externalName: httpbin.org

---

apiVersion: apisix.apache.org/v1alpha1

kind: PluginConfig

metadata:

  namespace: aic

  name: limit-count-plugin-config

spec:

  plugins:

    - name: limit-count

      config:

        count: 1

        time_window: 30

        rejected_code: 429

        policy: local

        group: srv1

    - name: proxy-rewrite

      config:

        uri: /get

---

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  namespace: aic

  name: limit-count-route-1

spec:

  parentRefs:

    - name: apisix

  rules:

    - matches:

        - path:

            type: Exact

            value: /get1

      filters:

        - type: ExtensionRef

          extensionRef:

            group: apisix.apache.org

            kind: PluginConfig

            name: limit-count-plugin-config

      backendRefs:

        - name: httpbin-external-domain

          port: 80

---

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  namespace: aic

  name: limit-count-route-2

spec:

  parentRefs:

    - name: apisix

  rules:

    - matches:

        - path:

            type: Exact

            value: /get2

      filters:

        - type: ExtensionRef

          extensionRef:

            group: apisix.apache.org

            kind: PluginConfig

            name: limit-count-plugin-config

      backendRefs:

        - name: httpbin-external-domain

          port: 80
```

Create an ApisixRoute with multiple paths that share the same plugin configuration:

limit-count-ic.yaml

```
apiVersion: apisix.apache.org/v2

kind: ApisixUpstream

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  ingressClassName: apisix

  externalNodes:

  - type: Domain

    name: httpbin.org

---

apiVersion: apisix.apache.org/v2

kind: ApisixRoute

metadata:

  namespace: aic

  name: limit-count-shared-route

spec:

  ingressClassName: apisix

  http:

    - name: limit-count-shared

      match:

        paths:

          - /get1

          - /get2

      upstreams:

      - name: httpbin-external-domain

      plugins:

        - name: proxy-rewrite

          enable: true

          config:

            uri: /get

        - name: limit-count

          enable: true

          config:

            count: 1

            time_window: 30

            rejected_code: 429

            policy: local

            group: srv1
```

Apply the configuration to your cluster:

```
kubectl apply -f limit-count-ic.yaml
```

note

The [`proxy-rewrite`](https://docs.api7.ai/hub/proxy-rewrite.md) plugin is used to rewrite the URI to `/get` so that requests are forwarded to the correct endpoint.

Send a request to route `/get1`:

```
curl -i "http://127.0.0.1:9080/get1"
```

You should see an `HTTP/1.1 200 OK` response with the corresponding response body.

Send the same request to route `/get2` within the same 30-second time interval:

```
curl -i "http://127.0.0.1:9080/get2"
```

You should receive an `HTTP/1.1 429 Too Many Requests` response, which verifies the two routes share the same rate limiting quota.

### Share Quota Among APISIX Nodes with a Redis Server[​](#share-quota-among-apisix-nodes-with-a-redis-server "Direct link to Share Quota Among APISIX Nodes with a Redis Server")

The following example demonstrates rate limiting across multiple APISIX nodes with a Redis server, so different APISIX nodes share the same rate limiting quota.

Configure the route with Redis connection details. Adjust the Redis host, credentials, and database for your environment.

* Admin API
* ADC
* Ingress Controller

```
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "limit-count-route",

    "uri": "/get",

    "plugins": {

      "limit-count": {

        "count": 1,

        "time_window": 30,

        "rejected_code": 429,

        "key": "remote_addr",

        "policy": "redis",

        "redis_host": "192.168.xxx.xxx",

        "redis_port": 6379,

        "redis_password": "p@ssw0rd",

        "redis_database": 1

      }

    },

    "upstream": {

      "type": "roundrobin",

      "nodes": {

        "httpbin.org:80": 1

      }

    }

  }'
```

Create a route with Redis-based rate limiting:

adc.yaml

```
services:

  - name: redis-limit-service

    routes:

      - name: redis-limit-route

        uris:

          - /get

        plugins:

          limit-count:

            count: 1

            time_window: 30

            rejected_code: 429

            key: remote_addr

            policy: redis

            redis_host: "192.168.xxx.xxx"

            redis_port: 6379

            redis_password: "p@ssw0rd"

            redis_database: 1

    upstream:

      type: roundrobin

      nodes:

        - host: httpbin.org

          port: 80

          weight: 1
```

Preview the changes and confirm that they only affect resources for this guide:

```
adc diff -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

Synchronize the configuration to the gateway:

```
adc sync -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

* Gateway API
* APISIX CRD

limit-count-ic.yaml

```
apiVersion: v1

kind: Service

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  type: ExternalName

  externalName: httpbin.org

---

apiVersion: apisix.apache.org/v1alpha1

kind: PluginConfig

metadata:

  namespace: aic

  name: limit-count-redis-plugin-config

spec:

  plugins:

    - name: limit-count

      config:

        count: 1

        time_window: 30

        rejected_code: 429

        key: remote_addr

        policy: redis

        redis_host: "redis-service.aic.svc"

        redis_port: 6379

        redis_password: "p@ssw0rd"

        redis_database: 1

---

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  namespace: aic

  name: redis-limit-route

spec:

  parentRefs:

    - name: apisix

  rules:

    - matches:

        - path:

            type: Exact

            value: /get

      filters:

        - type: ExtensionRef

          extensionRef:

            group: apisix.apache.org

            kind: PluginConfig

            name: limit-count-redis-plugin-config

      backendRefs:

        - name: httpbin-external-domain

          port: 80
```

limit-count-ic.yaml

```
apiVersion: apisix.apache.org/v2

kind: ApisixUpstream

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  ingressClassName: apisix

  externalNodes:

  - type: Domain

    name: httpbin.org

---

apiVersion: apisix.apache.org/v2

kind: ApisixRoute

metadata:

  namespace: aic

  name: redis-limit-route

spec:

  ingressClassName: apisix

  http:

    - name: redis-limit-route

      match:

        paths:

          - /get

      upstreams:

      - name: httpbin-external-domain

      plugins:

      - name: limit-count

        enable: true

        config:

          count: 1

          time_window: 30

          rejected_code: 429

          key: remote_addr

          policy: redis

          redis_host: "redis-service.aic.svc"

          redis_port: 6379

          redis_password: "p@ssw0rd"

          redis_database: 1
```

Apply the configuration to your cluster:

```
kubectl apply -f limit-count-ic.yaml
```

❶ `policy`: set to `redis` to use a Redis instance for rate limiting.

❷ `redis_host`: set to Redis instance IP address.

❸ `redis_port`: set to Redis instance listening port.

❹ `redis_password`: set to the password of the Redis instance, if any.

❺ `redis_database`: set to the database number in the Redis instance.

Send a request to an APISIX instance:

```
curl -i "http://127.0.0.1:9080/get"
```

You should see an `HTTP/1.1 200 OK` response with the corresponding response body.

Send the same request to a different APISIX instance within the same 30-second interval. You should receive an `HTTP/1.1 429 Too Many Requests` response, verifying that routes on different APISIX nodes share the same quota.

### Reduce Redis Round Trips with Delayed Synchronization[​](#reduce-redis-round-trips-with-delayed-synchronization "Direct link to Reduce Redis Round Trips with Delayed Synchronization")

By default, Redis-based policies synchronize the counter on every request. You can instead accumulate increments locally and synchronize them with Redis at a configured interval. Introduced in API7 Enterprise 3.9.14 and 3.10.1, and APISIX 3.18.0.

To enable delayed synchronization in the complete Redis example above, add `sync_interval: 1` to the `limit-count` configuration. The value is in seconds, must be at least `0.1`, and must be smaller than a numeric `time_window`. This option also works with the `redis-cluster` and `redis-sentinel` policies. Set it to `-1`, the default, to synchronize every request.

Delayed synchronization reduces Redis round trips, but counters on different workers or gateway nodes can temporarily diverge before their local increments are synchronized. As a result, the shared quota is not exact during the synchronization interval and concurrent requests can exceed the configured count. Use direct synchronization when exact cross-node enforcement is more important than reducing Redis traffic.

### Share Quota Among APISIX Nodes with a Redis Cluster[​](#share-quota-among-apisix-nodes-with-a-redis-cluster "Direct link to Share Quota Among APISIX Nodes with a Redis Cluster")

You can also use a Redis cluster to apply the same quota across multiple APISIX nodes, such that different APISIX nodes share the same rate limiting quota.

Ensure that your Redis instances are running in [cluster mode](https://redis.io/docs/management/scaling/#create-and-use-a-redis-cluster). A minimum of two nodes are required for the `limit-count` plugin configurations.

Configure the route with Redis cluster details. Adjust the cluster nodes, credentials, and cluster name for your environment.

* Admin API
* ADC
* Ingress Controller

```
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "limit-count-route",

    "uri": "/get",

    "plugins": {

      "limit-count": {

        "count": 1,

        "time_window": 30,

        "rejected_code": 429,

        "key": "remote_addr",

        "policy": "redis-cluster",

        "redis_cluster_nodes": [

          "192.168.xxx.xxx:6379",

          "192.168.xxx.xxx:16379"

        ],

        "redis_password": "p@ssw0rd",

        "redis_cluster_name": "redis-cluster",

        "redis_cluster_ssl": true

      }

    },

    "upstream": {

      "type": "roundrobin",

      "nodes": {

        "httpbin.org:80": 1

      }

    }

  }'
```

Create a route with Redis cluster-based rate limiting:

adc.yaml

```
services:

  - name: redis-cluster-limit-service

    routes:

      - name: redis-cluster-limit-route

        uris:

          - /get

        plugins:

          limit-count:

            count: 1

            time_window: 30

            rejected_code: 429

            key: remote_addr

            policy: redis-cluster

            redis_cluster_nodes:

              - "192.168.xxx.xxx:6379"

              - "192.168.xxx.xxx:16379"

            redis_password: "p@ssw0rd"

            redis_cluster_name: redis-cluster

            redis_cluster_ssl: true

    upstream:

      type: roundrobin

      nodes:

        - host: httpbin.org

          port: 80

          weight: 1
```

Preview the changes and confirm that they only affect resources for this guide:

```
adc diff -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

Synchronize the configuration to the gateway:

```
adc sync -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

* Gateway API
* APISIX CRD

limit-count-ic.yaml

```
apiVersion: v1

kind: Service

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  type: ExternalName

  externalName: httpbin.org

---

apiVersion: apisix.apache.org/v1alpha1

kind: PluginConfig

metadata:

  namespace: aic

  name: limit-count-redis-cluster-plugin-config

spec:

  plugins:

    - name: limit-count

      config:

        count: 1

        time_window: 30

        rejected_code: 429

        key: remote_addr

        policy: redis-cluster

        redis_cluster_nodes:

          - "redis-cluster-0.redis-cluster.aic.svc:6379"

          - "redis-cluster-1.redis-cluster.aic.svc:6379"

        redis_password: "p@ssw0rd"

        redis_cluster_name: redis-cluster

        redis_cluster_ssl: true

---

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  namespace: aic

  name: redis-cluster-limit-route

spec:

  parentRefs:

    - name: apisix

  rules:

    - matches:

        - path:

            type: Exact

            value: /get

      filters:

        - type: ExtensionRef

          extensionRef:

            group: apisix.apache.org

            kind: PluginConfig

            name: limit-count-redis-cluster-plugin-config

      backendRefs:

        - name: httpbin-external-domain

          port: 80
```

limit-count-ic.yaml

```
apiVersion: apisix.apache.org/v2

kind: ApisixUpstream

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  ingressClassName: apisix

  externalNodes:

  - type: Domain

    name: httpbin.org

---

apiVersion: apisix.apache.org/v2

kind: ApisixRoute

metadata:

  namespace: aic

  name: redis-cluster-limit-route

spec:

  ingressClassName: apisix

  http:

    - name: redis-cluster-limit-route

      match:

        paths:

          - /get

      upstreams:

      - name: httpbin-external-domain

      plugins:

      - name: limit-count

        enable: true

        config:

          count: 1

          time_window: 30

          rejected_code: 429

          key: remote_addr

          policy: redis-cluster

          redis_cluster_nodes:

            - "redis-cluster-0.redis-cluster.aic.svc:6379"

            - "redis-cluster-1.redis-cluster.aic.svc:6379"

          redis_password: "p@ssw0rd"

          redis_cluster_name: redis-cluster

          redis_cluster_ssl: true
```

Apply the configuration to your cluster:

```
kubectl apply -f limit-count-ic.yaml
```

❶ `policy`: set to `redis-cluster` to use a Redis cluster for rate limiting.

❷ `redis_cluster_nodes`: set to Redis node addresses in the Redis cluster.

❸ `redis_password`: set to the password of the Redis cluster, if any.

❹ `redis_cluster_name`: set to the Redis cluster name.

➎ `redis_cluster_ssl`: enable SSL/TLS communication with Redis cluster.

Send a request to an APISIX instance:

```
curl -i "http://127.0.0.1:9080/get"
```

You should see an `HTTP/1.1 200 OK` response with the corresponding response body.

Send the same request to a different APISIX instance within the same 30-second interval. You should receive an `HTTP/1.1 429 Too Many Requests` response, verifying that routes on different APISIX nodes share the same quota.

### Share Quota Among APISIX Nodes with Redis Sentinel[​](#share-quota-among-apisix-nodes-with-redis-sentinel "Direct link to Share Quota Among APISIX Nodes with Redis Sentinel")

Redis Sentinel provides a shared counter backend and discovers a promoted Redis master after failover. Introduced in API7 Enterprise 3.9.14 and 3.10.1, and APISIX 3.18.0.

Configure the route with Redis Sentinel details. Adjust the Sentinel nodes, credentials, and monitored master name for your environment.

* Admin API
* ADC
* Ingress Controller

```
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "limit-count-route",

    "uri": "/get",

    "plugins": {

      "limit-count": {

        "count": 1,

        "time_window": 30,

        "rejected_code": 429,

        "key": "remote_addr",

        "policy": "redis-sentinel",

        "redis_sentinels": [

          { "host": "192.168.xxx.xxx", "port": 26379 },

          { "host": "192.168.xxx.xxx", "port": 26380 }

        ],

        "redis_master_name": "mymaster",

        "sentinel_username": "sentinel-user",

        "sentinel_password": "p@ssw0rd",

        "redis_database": 1

      }

    },

    "upstream": {

      "type": "roundrobin",

      "nodes": {

        "httpbin.org:80": 1

      }

    }

  }'
```

adc.yaml

```
services:

  - name: httpbin

    routes:

      - name: limit-count-route

        uris:

          - /get

        plugins:

          limit-count:

            count: 1

            time_window: 30

            rejected_code: 429

            key: remote_addr

            policy: redis-sentinel

            redis_sentinels:

              - host: 192.168.xxx.xxx

                port: 26379

              - host: 192.168.xxx.xxx

                port: 26380

            redis_master_name: mymaster

            sentinel_username: sentinel-user

            sentinel_password: p@ssw0rd

            redis_database: 1

    upstream:

      type: roundrobin

      nodes:

        - host: httpbin.org

          port: 80

          weight: 1
```

Preview the changes and confirm that they only affect resources for this guide:

```
adc diff -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

Synchronize the configuration to the gateway:

```
adc sync -f adc.yaml \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

* Gateway API
* APISIX CRD

limit-count-ic.yaml

```
apiVersion: v1

kind: Service

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  type: ExternalName

  externalName: httpbin.org

---

apiVersion: apisix.apache.org/v1alpha1

kind: PluginConfig

metadata:

  namespace: aic

  name: limit-count-plugin-config

spec:

  plugins:

    - name: limit-count

      config:

        count: 1

        time_window: 30

        rejected_code: 429

        key: remote_addr

        policy: redis-sentinel

        redis_sentinels:

          - host: 192.168.xxx.xxx

            port: 26379

          - host: 192.168.xxx.xxx

            port: 26380

        redis_master_name: mymaster

        sentinel_username: sentinel-user

        sentinel_password: p@ssw0rd

        redis_database: 1

---

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  namespace: aic

  name: limit-count-route

spec:

  parentRefs:

    - name: apisix

  rules:

    - matches:

        - path:

            type: Exact

            value: /get

      filters:

        - type: ExtensionRef

          extensionRef:

            group: apisix.apache.org

            kind: PluginConfig

            name: limit-count-plugin-config

      backendRefs:

        - name: httpbin-external-domain

          port: 80
```

limit-count-ic.yaml

```
apiVersion: apisix.apache.org/v2

kind: ApisixUpstream

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  ingressClassName: apisix

  externalNodes:

    - type: Domain

      name: httpbin.org

---

apiVersion: apisix.apache.org/v2

kind: ApisixRoute

metadata:

  namespace: aic

  name: limit-count-route

spec:

  ingressClassName: apisix

  http:

    - name: limit-count-route

      match:

        paths:

          - /get

      upstreams:

        - name: httpbin-external-domain

      plugins:

        - name: limit-count

          enable: true

          config:

            count: 1

            time_window: 30

            rejected_code: 429

            key: remote_addr

            policy: redis-sentinel

            redis_sentinels:

              - host: 192.168.xxx.xxx

                port: 26379

              - host: 192.168.xxx.xxx

                port: 26380

            redis_master_name: mymaster

            sentinel_username: sentinel-user

            sentinel_password: p@ssw0rd

            redis_database: 1
```

Apply the configuration to your cluster:

```
kubectl apply -f limit-count-ic.yaml
```

Where:

* `policy` is set to `redis-sentinel` to use Redis nodes managed by Redis Sentinel.
* `redis_sentinels` lists the Sentinel nodes, each with a `host` and `port`.
* `redis_master_name` is the name of the master that Sentinel monitors.
* `sentinel_username` and `sentinel_password` authenticate with the Sentinel nodes, if required.

Send a request to an APISIX instance:

```
curl -i "http://127.0.0.1:9080/get"
```

You should see an `HTTP/1.1 200 OK` response with the corresponding response body.

Send the same request to a different APISIX instance within the same 30-second interval. You should receive an `HTTP/1.1 429 Too Many Requests` response, verifying that routes on different APISIX nodes share the same quota.

### Rate Limit with Anonymous Consumer[​](#rate-limit-with-anonymous-consumer "Direct link to Rate Limit with Anonymous Consumer")

The following example demonstrates how you can configure different rate limiting policies by regular and anonymous consumers, where the anonymous consumer does not need to authenticate and has less quota. While this example uses [`key-auth`](https://docs.api7.ai/hub/key-auth.md) for authentication, the anonymous consumer can also be configured with [`basic-auth`](https://docs.api7.ai/hub/basic-auth.md), [`jwt-auth`](https://docs.api7.ai/hub/jwt-auth.md), and [`hmac-auth`](https://docs.api7.ai/hub/hmac-auth.md).

* Admin API
* ADC
* Ingress Controller

Create consumer `john` with a quota of 3:

```
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "username": "john",

    "plugins": {

      "limit-count": {

        "count": 3,

        "time_window": 30,

        "rejected_code": 429,

        "policy": "local"

      }

    }

  }'
```

Create `key-auth` credential for consumer `john`:

```
curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "cred-john-key-auth",

    "plugins": {

      "key-auth": {

        "key": "john-key"

      }

    }

  }'
```

Create an anonymous user `anonymous` with a quota of 1:

```
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "username": "anonymous",

    "plugins": {

      "limit-count": {

        "count": 1,

        "time_window": 30,

        "rejected_code": 429,

        "policy": "local"

      }

    }

  }'
```

Create a route with `key-auth` plugin that accepts anonymous consumer:

```
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "key-auth-route",

    "uri": "/anything",

    "plugins": {

      "key-auth": {

        "anonymous_consumer": "anonymous"

      }

    },

    "upstream": {

      "type": "roundrobin",

      "nodes": {

        "httpbin.org:80": 1

      }

    }

  }'
```

Configure consumers with different rate limits and a route that accepts anonymous users:

adc.yaml

```
consumers:

  - username: john

    plugins:

      limit-count:

        count: 3

        time_window: 30

        rejected_code: 429

        policy: local

    credentials:

      - name: key-auth

        type: key-auth

        config:

          key: john-key

  - username: anonymous

    plugins:

      limit-count:

        count: 1

        time_window: 30

        rejected_code: 429

        policy: local

services:

  - name: anonymous-rate-limit-service

    routes:

      - name: key-auth-route

        uris:

          - /anything

        plugins:

          key-auth:

            anonymous_consumer: anonymous

    upstream:

      type: roundrobin

      nodes:

        - host: httpbin.org

          port: 80

          weight: 1
```

Preview the changes and confirm that they only affect resources for this guide:

```
adc diff -f adc.yaml \

  --include-resource-type consumer \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

Synchronize the configuration to the gateway:

```
adc sync -f adc.yaml \

  --include-resource-type consumer \

  --include-resource-type service \

  --label-selector docs-example=limit-count
```

* Gateway API
* APISIX CRD

Configure consumers with different rate limits and a route that accepts anonymous users:

limit-count-ic.yaml

```
apiVersion: apisix.apache.org/v1alpha1

kind: Consumer

metadata:

  namespace: aic

  name: john

spec:

  gatewayRef:

    name: apisix

  credentials:

    - type: key-auth

      name: primary-key

      config:

        key: john-key

  plugins:

    - name: limit-count

      config:

        count: 3

        time_window: 30

        rejected_code: 429

        policy: local

---

apiVersion: apisix.apache.org/v1alpha1

kind: Consumer

metadata:

  namespace: aic

  name: anonymous

spec:

  gatewayRef:

    name: apisix

  plugins:

    - name: limit-count

      config:

        count: 1

        time_window: 30

        rejected_code: 429

        policy: local

---

apiVersion: v1

kind: Service

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  type: ExternalName

  externalName: httpbin.org

---

apiVersion: apisix.apache.org/v1alpha1

kind: PluginConfig

metadata:

  namespace: aic

  name: key-auth-plugin-config

spec:

  plugins:

    - name: key-auth

      config:

        anonymous_consumer: aic_anonymous  # namespace_consumername

---

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  namespace: aic

  name: key-auth-route

spec:

  parentRefs:

    - name: apisix

  rules:

    - matches:

        - path:

            type: Exact

            value: /anything

      filters:

        - type: ExtensionRef

          extensionRef:

            group: apisix.apache.org

            kind: PluginConfig

            name: key-auth-plugin-config

      backendRefs:

        - name: httpbin-external-domain

          port: 80
```

Apply the configuration to your cluster:

```
kubectl apply -f limit-count-ic.yaml
```

limit-count-apisix-crd.yaml

```
apiVersion: apisix.apache.org/v2

kind: ApisixConsumer

metadata:

  namespace: aic

  name: john

spec:

  ingressClassName: apisix

  authParameter:

    keyAuth:

      value:

        key: john-key

  plugins:

    - name: limit-count

      config:

        count: 3

        time_window: 30

        rejected_code: 429

        policy: local

---

apiVersion: apisix.apache.org/v2

kind: ApisixConsumer

metadata:

  namespace: aic

  name: anonymous

spec:

  ingressClassName: apisix

  plugins:

    - name: limit-count

      config:

        count: 1

        time_window: 30

        rejected_code: 429

        policy: local

---

apiVersion: apisix.apache.org/v2

kind: ApisixUpstream

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  ingressClassName: apisix

  externalNodes:

    - type: Domain

      name: httpbin.org

---

apiVersion: apisix.apache.org/v2

kind: ApisixRoute

metadata:

  namespace: aic

  name: key-auth-route

spec:

  ingressClassName: apisix

  http:

    - name: anything

      match:

        paths:

          - /anything

      upstreams:

        - name: httpbin-external-domain

      plugins:

        - name: key-auth

          config:

            anonymous_consumer: aic_anonymous
```

Apply the configuration to your cluster:

```
kubectl apply -f limit-count-apisix-crd.yaml
```

To verify, send five consecutive requests with `john`'s key:

```
resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H 'apikey: john-key' -o /dev/null -s -w "%{http_code}\n") && \

  count_200=$(echo "$resp" | grep "200" | wc -l) && \

  count_429=$(echo "$resp" | grep "429" | wc -l) && \

  echo "200": $count_200, "429": $count_429
```

You should see the following response, showing that out of the 5 requests, 3 requests were successful (status code 200) while the others were rejected (status code 429).

```
200:    3, 429:    2
```

Send five anonymous requests:

```
resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -o /dev/null -s -w "%{http_code}\n") && \

  count_200=$(echo "$resp" | grep "200" | wc -l) && \

  count_429=$(echo "$resp" | grep "429" | wc -l) && \

  echo "200": $count_200, "429": $count_429
```

You should see the following response, showing that only one request was successful:

```
200:    1, 429:    4
```

### Customize Rate Limiting Headers[​](#customize-rate-limiting-headers "Direct link to Customize Rate Limiting Headers")

The following example demonstrates how you can use plugin metadata to customize the rate limiting response header names, which are by default `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`.

* Admin API
* ADC
* Ingress Controller

Configure plugin metadata to customize rate limiting headers:

```
curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/limit-count" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

  "limit_header": "X-Custom-RateLimit-Limit",

  "remaining_header": "X-Custom-RateLimit-Remaining",

  "reset_header": "X-Custom-RateLimit-Reset"

}'
```

Create a route with `limit-count` plugin:

```
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \

  -H "X-API-KEY: ${ADMIN_API_KEY}" \

  -d '{

    "id": "limit-count-route",

    "uri": "/anything/limit-count-metadata",

    "plugins": {

      "limit-count": {

        "count": 1,

        "time_window": 30,

        "rejected_code": 429,

        "key_type": "var",

        "key": "remote_addr",

        "policy": "local"

      }

    },

    "upstream": {

      "type": "roundrobin",

      "nodes": {

        "httpbin.org:80": 1

      }

    }

  }'
```

Export the complete plugin metadata collection:

```
adc dump -o metadata.yaml --with-id --include-resource-type plugin_metadata
```

Update the following fields in the exported file, preserving other metadata entries and any existing rate limiting settings:

metadata.yaml

```
plugin_metadata:

  limit-count:

    limit_header: X-Custom-RateLimit-Limit

    remaining_header: X-Custom-RateLimit-Remaining

    reset_header: X-Custom-RateLimit-Reset
```

Preview the metadata changes and confirm that no unrelated entries are modified or deleted:

```
adc diff -f metadata.yaml --include-resource-type plugin_metadata
```

Synchronize the reviewed metadata:

```
adc sync -f metadata.yaml --include-resource-type plugin_metadata
```

Create a route with rate limiting:

adc.yaml

```
services:

  - name: limit-count-metadata-service

    labels:

      example: limit-count-metadata

    routes:

      - name: limit-count-metadata-route

        uris:

          - /anything/limit-count-metadata

        plugins:

          limit-count:

            count: 1

            time_window: 30

            rejected_code: 429

            key_type: var

            key: remote_addr

            policy: local

    upstream:

      type: roundrobin

      nodes:

        - host: httpbin.org

          port: 80

          weight: 1
```

Preview changes to the service for this example:

```
adc diff -f adc.yaml \

  --include-resource-type service \

  --label-selector example=limit-count-metadata
```

Synchronize the reviewed service configuration:

```
adc sync -f adc.yaml \

  --include-resource-type service \

  --label-selector example=limit-count-metadata
```

Update your GatewayProxy manifest for the plugin metadata:

gatewayproxy.yaml

```
apiVersion: apisix.apache.org/v1alpha1

kind: GatewayProxy

metadata:

  namespace: aic

  name: apisix-config

spec:

  provider:

    type: ControlPlane

    controlPlane:

      # ...

      # your control plane connection configuration

  pluginMetadata:

    limit-count:

      limit_header: X-Custom-RateLimit-Limit

      remaining_header: X-Custom-RateLimit-Remaining

      reset_header: X-Custom-RateLimit-Reset
```

* Gateway API
* APISIX CRD

Create a route with the plugin enabled:

limit-count-ic.yaml

```
apiVersion: v1

kind: Service

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  type: ExternalName

  externalName: httpbin.org

---

apiVersion: apisix.apache.org/v1alpha1

kind: PluginConfig

metadata:

  namespace: aic

  name: limit-count-plugin-config

spec:

  plugins:

    - name: limit-count

      config:

        count: 1

        time_window: 30

        rejected_code: 429

        key_type: var

        key: remote_addr

        policy: local

---

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  namespace: aic

  name: limit-count-route

spec:

  parentRefs:

    - name: apisix

  rules:

    - matches:

        - path:

            type: Exact

            value: /anything/limit-count-metadata

      filters:

        - type: ExtensionRef

          extensionRef:

            group: apisix.apache.org

            kind: PluginConfig

            name: limit-count-plugin-config

      backendRefs:

        - name: httpbin-external-domain

          port: 80
```

Create a route with the plugin enabled:

limit-count-ic.yaml

```
apiVersion: apisix.apache.org/v2

kind: ApisixUpstream

metadata:

  namespace: aic

  name: httpbin-external-domain

spec:

  ingressClassName: apisix

  externalNodes:

  - type: Domain

    name: httpbin.org

---

apiVersion: apisix.apache.org/v2

kind: ApisixRoute

metadata:

  namespace: aic

  name: limit-count-route

spec:

  ingressClassName: apisix

  http:

    - name: limit-count-route

      match:

        paths:

          - /anything/limit-count-metadata

      upstreams:

      - name: httpbin-external-domain

      plugins:

      - name: limit-count

        enable: true

        config:

          count: 1

          time_window: 30

          rejected_code: 429

          key_type: var

          key: remote_addr

          policy: local
```

Apply the configuration to your cluster:

```
kubectl apply -f gatewayproxy.yaml -f limit-count-ic.yaml
```

Send a request to verify:

```
curl -i "http://127.0.0.1:9080/anything/limit-count-metadata"
```

You should receive an `HTTP/1.1 200 OK` response and see the following headers:

```
X-Custom-RateLimit-Limit: 1

X-Custom-RateLimit-Remaining: 0

X-Custom-RateLimit-Reset: 28
```
