API7 Docs

limit-req plugin for Apache APISIX

Skill for configuring the Apache APISIX limit-req plugin via the a6 CLI. Covers leaky-bucket rate limiting, rate/burst configuration, nodelay behavior,…

Overview

The limit-req plugin rate-limits requests using the leaky bucket algorithm. Unlike limit-count (fixed window), it provides smooth traffic shaping by throttling burst requests with configurable delays. This prevents traffic spikes from overwhelming upstream services.

When to Use

  • Smooth traffic to protect upstream from sudden spikes
  • Enforce per-second QPS limits
  • Throttle (delay) excess requests instead of rejecting them immediately
  • Combine with limit-count for both per-second and per-hour limits

Plugin Configuration Reference

Core Fields

FieldTypeRequiredDefaultDescription
ratenumberYesSustained requests per second (QPS). > 0
burstnumberYesExtra burst capacity above rate. >= 0
keystringYesVariable to count requests by
key_typestringNo"var"Key type: "var" or "var_combination"
rejected_codeintegerNo503HTTP status on rejection (200–599)
rejected_msgstringNoCustom rejection message body
nodelaybooleanNofalseIf true, don't delay burst requests
allow_degradationbooleanNofalseAllow requests when plugin fails
policystringNo"local"Storage: "local", "redis", or "redis-cluster"

Redis Fields (when policy: "redis")

FieldTypeRequiredDefault
redis_hoststringYes
redis_portintegerNo6379
redis_usernamestringNo
redis_passwordstringNo
redis_databaseintegerNo0
redis_timeoutintegerNo1000
redis_sslbooleanNofalse

Redis Cluster Fields (when policy: "redis-cluster")

FieldTypeRequiredDefault
redis_cluster_nodesarray[string]Yes
redis_cluster_namestringYes
redis_passwordstringNo
redis_timeoutintegerNo1000
redis_cluster_sslbooleanNofalse

Leaky Bucket Algorithm

Incoming requests → [   Bucket (burst capacity)   ] → Leak at 'rate' per second → Upstream
                    ↓ overflow (> rate + burst)
                    Rejected (503/429)
Request RateBehavior
rateProcessed immediately
> rate but ≤ rate + burstDelayed (smoothed) if nodelay: false; immediate if nodelay: true
> rate + burstRejected with rejected_code

nodelay Explained

  • nodelay: false (default): Burst requests are delayed (APISIX sleeps) to smooth traffic. Higher latency for burst requests but protects upstream.
  • nodelay: true: Burst requests are processed immediately without delay. Better latency but upstream sees spikes up to rate + burst.

Step-by-Step: Basic Rate Limiting

1. Strict QPS limit (no burst)

a6 route create -f - <<'EOF'
{
  "id": "strict-qps",
  "uri": "/api/*",
  "plugins": {
    "limit-req": {
      "rate": 10,
      "burst": 0,
      "key": "remote_addr",
      "rejected_code": 429,
      "nodelay": true
    }
  },
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "backend:8080": 1
    }
  }
}
EOF

10 requests per second per IP. Anything above is immediately rejected.

2. Smooth traffic with burst allowance

a6 route create -f - <<'EOF'
{
  "id": "smooth-api",
  "uri": "/api/*",
  "plugins": {
    "limit-req": {
      "rate": 5,
      "burst": 10,
      "key": "remote_addr",
      "rejected_code": 429
    }
  },
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "backend:8080": 1
    }
  }
}
EOF
  • 5 req/s sustained rate
  • Up to 10 extra burst requests (delayed to smooth traffic)
  • Requests above 15/s rejected with 429

Common Patterns

Multi-variable key

{
  "plugins": {
    "limit-req": {
      "rate": 10,
      "burst": 5,
      "key_type": "var_combination",
      "key": "$remote_addr $http_x_api_version",
      "rejected_code": 429
    }
  }
}

Separate buckets per (IP + API version header) combination.

Combine limit-req + limit-count

{
  "plugins": {
    "limit-req": {
      "rate": 10,
      "burst": 20,
      "key": "remote_addr",
      "rejected_code": 429
    },
    "limit-count": {
      "count": 1000,
      "time_window": 3600,
      "key": "remote_addr",
      "rejected_code": 429
    }
  }
}
  • limit-req: Smooths per-second traffic (10 QPS with burst)
  • limit-count: Enforces hourly quota (1000/hour)

This prevents both short-term spikes and long-term abuse.

Distributed rate limiting with Redis

{
  "plugins": {
    "limit-req": {
      "rate": 100,
      "burst": 50,
      "key": "remote_addr",
      "policy": "redis",
      "redis_host": "redis.example.com",
      "redis_port": 6379,
      "redis_password": "secret",
      "rejected_code": 429
    }
  }
}

limit-req vs limit-count

Aspectlimit-reqlimit-count
AlgorithmLeaky bucketFixed window
UnitRequests per secondRequests per time window
Burst handlingDelays or allowsHard reject
Traffic shapingSmoothBursty at window boundaries
Response headersNoneX-RateLimit-*
Group supportNoYes
Best forQPS protection, traffic smoothingQuota enforcement, API plans

Troubleshooting

SymptomCauseFix
High latency on burstnodelay: false delays requestsSet nodelay: true for lower latency
All burst requests rejectedburst: 0Increase burst to allow some excess
No rate limit headerslimit-req doesn't add headersUse limit-count if headers needed
Limits not shared across nodespolicy: "local"Switch to "redis" or "redis-cluster"
Key empty, single bucket for allVariable doesn't existVerify key variable name

Config Sync Example

version: "1"
routes:
  - id: smooth-api
    uri: /api/*
    plugins:
      limit-req:
        rate: 10
        burst: 20
        key: remote_addr
        rejected_code: 429
    upstream_id: api-upstream
upstreams:
  - id: api-upstream
    type: roundrobin
    nodes:
      "backend:8080": 1

This page is generated from a6-plugin-limit-req/SKILL.md in the api7/a6 repository. Browse all skills on the AI Agent Skills page.