TraefikRateLimiter

package module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 19, 2026 License: MIT Imports: 8 Imported by: 0

README

TraefikRateLimiter

URL-level rate limiting middleware for Traefik, backed by in-memory fixed-window counters.

Go Version

English · 中文


Overview

TraefikRateLimiter is a Traefik middleware plugin that enforces URL-level rate limits using a fixed-window algorithm with in-process memory storage. It supports per-path rules (exact or prefix match), HTTP method filters, a configurable IP extraction strategy, and access-log friendly response headers.

Note: Because counters live in memory, each Traefik instance maintains independent state. For distributed rate limiting across multiple replicas, a shared backend (e.g. Redis) would be required.

Features

  • 🚦 URL-level rate limiting – exact or prefix path matching
  • 🔧 Method filtering – apply rules only to specific HTTP methods
  • ⏱️ Flexible time windows – s, m, h, d (e.g. 10s, 5m, 2h, 1d)
  • 🌐 Configurable IP strategy – custom header, depth, and fallback headers
  • 📊 Access-log friendly headers – expose Used, Remaining, RetryAfter, and Key
  • 🔋 Zero dependencies – standard library only, fully Yaegi-compatible
  • 🗂️ Default + per-rule limits – global fallback with fine-grained overrides

Installation

Option A — Local Plugin

Copy (or symlink) this repository into Traefik's local plugin directory:

/plugins-local/src/github.com/OVINC-CN/TraefikRateLimiter/

Then register it in your static Traefik configuration:

# traefik.yml (static config)
experimental:
  localPlugins:
    traefikratelimiter:
      moduleName: github.com/OVINC-CN/TraefikRateLimiter
Option B — Plugin Catalog
# traefik.yml (static config)
experimental:
  plugins:
    traefikratelimiter:
      moduleName: github.com/OVINC-CN/TraefikRateLimiter
      version: v0.1.0

Configuration

Full Example
# dynamic config
http:
  middlewares:
    my-ratelimit:
      plugin:
        traefikratelimiter:
          ipStrategy:
            header: "X-Forwarded-For"   # primary IP header (default)
            depth: 0                    # 0 = left-most; >0 = N-th from right
            trustedHeaders:             # fallback headers, tried in order
              - "X-Real-IP"
          default:                      # catch-all when no rule matches
            requests: 100
            period: "1m"
          rules:
            - name: "login"             # optional; used in X-RateLimit-Key
              path: "/api/v1/login"
              matchType: "exact"        # exact | prefix  (default: exact)
              methods: ["POST"]         # omit to match all methods
              requests: 5
              period: "1m"
            - path: "/api/"
              matchType: "prefix"
              requests: 60
              period: "1s"

  routers:
    api:
      rule: "PathPrefix(`/`)"
      service: my-service
      middlewares:
        - my-ratelimit
Configuration Reference
Field Type Default Description
ipStrategy.header string X-Forwarded-For Primary header to read the client IP from
ipStrategy.depth int 0 0 = left-most entry; >0 = N-th from right
ipStrategy.trustedHeaders []string [] Fallback headers tried after the primary one
default.requests int — Max requests per window for unmatched routes
default.period string — Window size for the default limit
rules[].name string r{index} Optional label (appears in X-RateLimit-Key)
rules[].path string required Path to match
rules[].matchType string exact exact or prefix
rules[].methods []string all HTTP methods to apply the rule to
rules[].requests int required Max requests allowed per window
rules[].period string required Window size (s / m / h / d)
addHeaders bool true Write X-RateLimit-* headers to the response

At least one of default or rules must be configured; New returns an error otherwise.

Rate Limit Dimensions

The internal counter key is:

{ruleID} | {ip} | {realPath} | {windowIndex}

For a prefix rule on /api/, the paths /api/a and /api/b maintain separate counters — this is intentional and matches the expectation of URL-level rate limiting.

Response Headers

These headers are set on every matched request, whether allowed or rejected:

Header Example Description
X-RateLimit-Limit 60 Maximum requests configured for the window
X-RateLimit-Key login|10.0.0.1|/api/v1/login Rate-limit dimension identifier
X-RateLimit-Used 1/1h Requests used in this window / period
X-RateLimit-Remaining 59/1h Remaining requests / period
X-RateLimit-RetryAfter 0s Seconds until the window resets (with s suffix)
X-RateLimit-Reset 1713512345 Window reset time as a Unix timestamp
Rate-Limited Response (429)
HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8
Retry-After: 42
X-RateLimit-Used: 6/1m
X-RateLimit-Remaining: 0/1m
X-RateLimit-RetryAfter: 42s

{"error_code":"RATE_LIMITED","error_msg":"请求过于频繁,请 42 秒后重试"}

Access Log Integration

To surface rate-limit headers in Traefik's access log, add the following to your static configuration:

accessLog:
  filePath: "/var/log/traefik/access.log"
  format: json
  fields:
    headers:
      defaultMode: drop
      names:
        X-RateLimit-Key: keep
        X-RateLimit-Used: keep
        X-RateLimit-Remaining: keep
        X-RateLimit-RetryAfter: keep

Example log entry:

{
  "RequestPath": "/api/v1/login",
  "DownstreamStatus": 429,
  "request_X-RateLimit-Key": "login|10.0.0.1|/api/v1/login",
  "request_X-RateLimit-Used": "6/1m",
  "request_X-RateLimit-Remaining": "0/1m",
  "request_X-RateLimit-RetryAfter": "42s"
}

The exact header field prefix (request_ vs downstream_) depends on your Traefik version and configuration. Check your environment's actual output.

Known Limitations

Limitation Detail
Single-process only Counters are in-memory and not shared across Traefik replicas.
Fixed-window bursting Classic "double-hit" at window boundaries is possible.
No persistence Counters reset on process restart.

Development

# Lint & vet
go vet ./...

# Build
go build ./...

# Test
go test ./...

License

MIT © OVINC

Documentation

Overview

Package traefikratelimiter provides a URL-level, in-memory rate limiting middleware for Traefik with access-log friendly response headers.

Index

Constants

View Source
const HeaderKey = "X-RateLimit-Key"

HeaderKey exposes the internal rate-limit key used for this request, formatted as "{ruleID}|{ip}|{realPath}". Useful for debugging and access log correlation.

View Source
const HeaderLimit = "X-RateLimit-Limit"

HeaderLimit exposes the configured request budget for the window.

View Source
const HeaderRemaining = "X-RateLimit-Remaining"

HeaderRemaining exposes the remaining quota, formatted as "<remaining>/<period>".

View Source
const HeaderReset = "X-RateLimit-Reset"

HeaderReset exposes the absolute reset time as a unix timestamp.

View Source
const HeaderRetryAfter = "X-RateLimit-RetryAfter"

HeaderRetryAfter exposes the seconds until the window resets, formatted with a trailing "s" (e.g. "0s").

View Source
const HeaderUsed = "X-RateLimit-Used"

HeaderUsed exposes the in-window used count, formatted as "<count>/<period>".

Variables

This section is empty.

Functions

func New

func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error)

New is the constructor required by the Traefik plugin loader.

Types

type Config

type Config struct {
	IPStrategy IPStrategyConfig `json:"ipStrategy,omitempty" yaml:"ipStrategy,omitempty" toml:"ipStrategy,omitempty"`
	Default    LimitConfig      `json:"default,omitempty"    yaml:"default,omitempty"    toml:"default,omitempty"`
	Rules      []RuleConfig     `json:"rules,omitempty"      yaml:"rules,omitempty"      toml:"rules,omitempty"`
	// AddHeaders controls whether rate-limit response headers are written.
	// Defaults to true. Set to false to suppress all X-RateLimit-* headers.
	AddHeaders *bool `json:"addHeaders,omitempty" yaml:"addHeaders,omitempty" toml:"addHeaders,omitempty"`
}

Config is the root configuration consumed by the plugin.

func CreateConfig

func CreateConfig() *Config

CreateConfig returns a Config populated with sensible defaults.

type IPStrategyConfig

type IPStrategyConfig struct {
	// Header is the primary header name to read the client IP from. When
	// empty, "X-Forwarded-For" is used.
	Header string `json:"header,omitempty" yaml:"header,omitempty" toml:"header,omitempty"`
	// Depth controls which entry of a comma separated header value is used.
	// 0 (default) means the left-most entry (original client). A positive
	// value counts from the right (1 = right-most).
	Depth int `json:"depth,omitempty" yaml:"depth,omitempty" toml:"depth,omitempty"`
	// TrustedHeaders is an ordered list of additional headers tried after
	// Header. The first non-empty header wins.
	TrustedHeaders []string `json:"trustedHeaders,omitempty" yaml:"trustedHeaders,omitempty" toml:"trustedHeaders,omitempty"`
}

IPStrategyConfig defines how the client IP is extracted from the request.

type LimitConfig

type LimitConfig struct {
	// Requests is the maximum number of requests allowed during the window.
	Requests int64 `json:"requests,omitempty" yaml:"requests,omitempty" toml:"requests,omitempty"`
	// Period is the size of the fixed window. Examples: "10s", "1m", "2h", "1d".
	Period string `json:"period,omitempty" yaml:"period,omitempty" toml:"period,omitempty"`
}

LimitConfig is the limit definition shared by the default block and individual rules.

type MatchType

type MatchType string

MatchType describes how a rule path is compared against the request URL.

const (
	MatchExact  MatchType = "exact"
	MatchPrefix MatchType = "prefix"
)

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter is the Traefik middleware implementation.

func (*RateLimiter) ServeHTTP

func (rl *RateLimiter) ServeHTTP(rw http.ResponseWriter, req *http.Request)

type RuleConfig

type RuleConfig struct {
	Requests int64  `json:"requests,omitempty"  yaml:"requests,omitempty"  toml:"requests,omitempty"`
	Period   string `json:"period,omitempty"    yaml:"period,omitempty"    toml:"period,omitempty"`
	// Name is optional and is included in the internal counter key for
	// readability; defaults to "r{index}".
	Name      string   `json:"name,omitempty"      yaml:"name,omitempty"      toml:"name,omitempty"`
	Path      string   `json:"path,omitempty"      yaml:"path,omitempty"      toml:"path,omitempty"`
	MatchType string   `json:"matchType,omitempty" yaml:"matchType,omitempty" toml:"matchType,omitempty"`
	Methods   []string `json:"methods,omitempty"   yaml:"methods,omitempty"   toml:"methods,omitempty"`
}

RuleConfig describes a single per-path rule.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL