{"id":13592211,"url":"https://github.com/ulule/limiter","last_synced_at":"2025-05-13T16:07:25.958Z","repository":{"id":1753461,"uuid":"43542144","full_name":"ulule/limiter","owner":"ulule","description":"Dead simple rate limit middleware for Go.","archived":false,"fork":false,"pushed_at":"2024-12-10T12:15:49.000Z","size":402,"stargazers_count":2181,"open_issues_count":14,"forks_count":155,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-04-08T23:13:44.320Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ulule.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-10-02T08:12:38.000Z","updated_at":"2025-04-08T04:20:40.000Z","dependencies_parsed_at":"2024-01-08T16:08:46.705Z","dependency_job_id":"c70a0797-6bea-4d3a-a690-e2c25904e92b","html_url":"https://github.com/ulule/limiter","commit_stats":{"total_commits":331,"total_committers":27,"mean_commits":12.25925925925926,"dds":"0.49848942598187307","last_synced_commit":"c3db9d65aeed3f147a7b45e0ba412cf958e11dc8"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulule%2Flimiter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulule%2Flimiter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulule%2Flimiter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulule%2Flimiter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ulule","download_url":"https://codeload.github.com/ulule/limiter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250513606,"owners_count":21443202,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T16:01:06.961Z","updated_at":"2025-04-23T20:47:35.317Z","avatar_url":"https://github.com/ulule.png","language":"Go","readme":"# Limiter\n\n[![Documentation][godoc-img]][godoc-url]\n![License][license-img]\n[![Build Status][circle-img]][circle-url]\n[![Go Report Card][goreport-img]][goreport-url]\n\n_Dead simple rate limit middleware for Go._\n\n- Simple API\n- \"Store\" approach for backend\n- Redis support (but not tied too)\n- Middlewares: HTTP, [FastHTTP][6] and [Gin][4]\n\n## Installation\n\nUsing [Go Modules](https://github.com/golang/go/wiki/Modules)\n\n```bash\n$ go get github.com/ulule/limiter/v3@v3.11.2\n```\n\n## Usage\n\nIn five steps:\n\n- Create a `limiter.Rate` instance _(the number of requests per period)_\n- Create a `limiter.Store` instance _(see [Redis](https://github.com/ulule/limiter/blob/master/drivers/store/redis/store.go) or [In-Memory](https://github.com/ulule/limiter/blob/master/drivers/store/memory/store.go))_\n- Create a `limiter.Limiter` instance that takes store and rate instances as arguments\n- Create a middleware instance using the middleware of your choice\n- Give the limiter instance to your middleware initializer\n\n**Example:**\n\n```go\n// Create a rate with the given limit (number of requests) for the given\n// period (a time.Duration of your choice).\nimport \"github.com/ulule/limiter/v3\"\n\nrate := limiter.Rate{\n    Period: 1 * time.Hour,\n    Limit:  1000,\n}\n\n// You can also use the simplified format \"\u003climit\u003e-\u003cperiod\u003e\"\", with the given\n// periods:\n//\n// * \"S\": second\n// * \"M\": minute\n// * \"H\": hour\n// * \"D\": day\n//\n// Examples:\n//\n// * 5 reqs/second: \"5-S\"\n// * 10 reqs/minute: \"10-M\"\n// * 1000 reqs/hour: \"1000-H\"\n// * 2000 reqs/day: \"2000-D\"\n//\nrate, err := limiter.NewRateFromFormatted(\"1000-H\")\nif err != nil {\n    panic(err)\n}\n\n// Then, create a store. Here, we use the bundled Redis store. Any store\n// compliant to limiter.Store interface will do the job. The defaults are\n// \"limiter\" as Redis key prefix and a maximum of 3 retries for the key under\n// race condition.\nimport \"github.com/ulule/limiter/v3/drivers/store/redis\"\n\nstore, err := redis.NewStore(client)\nif err != nil {\n    panic(err)\n}\n\n// Alternatively, you can pass options to the store with the \"WithOptions\"\n// function. For example, for Redis store:\nimport \"github.com/ulule/limiter/v3/drivers/store/redis\"\n\nstore, err := redis.NewStoreWithOptions(pool, limiter.StoreOptions{\n    Prefix:   \"your_own_prefix\",\n})\nif err != nil {\n    panic(err)\n}\n\n// Or use a in-memory store with a goroutine which clears expired keys.\nimport \"github.com/ulule/limiter/v3/drivers/store/memory\"\n\nstore := memory.NewStore()\n\n// Then, create the limiter instance which takes the store and the rate as arguments.\n// Now, you can give this instance to any supported middleware.\ninstance := limiter.New(store, rate)\n\n// Alternatively, you can pass options to the limiter instance with several options.\ninstance := limiter.New(store, rate, limiter.WithClientIPHeader(\"True-Client-IP\"), limiter.WithIPv6Mask(mask))\n\n// Finally, give the limiter instance to your middleware initializer.\nimport \"github.com/ulule/limiter/v3/drivers/middleware/stdlib\"\n\nmiddleware := stdlib.NewMiddleware(instance)\n```\n\nSee middleware examples:\n\n- [HTTP](https://github.com/ulule/limiter-examples/tree/master/http/main.go)\n- [Gin](https://github.com/ulule/limiter-examples/tree/master/gin/main.go)\n- [Beego](https://github.com/ulule/limiter-examples/blob/master//beego/main.go)\n- [Chi](https://github.com/ulule/limiter-examples/tree/master/chi/main.go)\n- [Echo](https://github.com/ulule/limiter-examples/tree/master/echo/main.go)\n- [Fasthttp](https://github.com/ulule/limiter-examples/tree/master/fasthttp/main.go)\n\n## How it works\n\nThe ip address of the request is used as a key in the store.\n\nIf the key does not exist in the store we set a default\nvalue with an expiration period.\n\nYou will find two stores:\n\n- Redis: rely on [TTL](http://redis.io/commands/ttl) and incrementing the rate limit on each request.\n- In-Memory: rely on a fork of [go-cache](https://github.com/patrickmn/go-cache) with a goroutine to clear expired keys using a default interval.\n\nWhen the limit is reached, a `429` HTTP status code is sent.\n\n## Limiter behind a reverse proxy\n\n### Introduction\n\nIf your limiter is behind a reverse proxy, it could be difficult to obtain the \"real\" client IP.\n\nSome reverse proxies, like AWS ALB, lets all header values through that it doesn't set itself.\nLike for example, `True-Client-IP` and `X-Real-IP`.\nSimilarly, `X-Forwarded-For` is a list of comma-separated IPs that gets appended to by each traversed proxy.\nThe idea is that the first IP _(added by the first proxy)_ is the true client IP. Each subsequent IP is another proxy along the path.\n\nAn attacker can spoof either of those headers, which could be reported as a client IP.\n\nBy default, limiter doesn't trust any of those headers: you have to explicitly enable them in order to use them.\nIf you enable them, **you must always be aware** that any header added by any _(reverse)_ proxy not controlled\nby you **are completely unreliable.**\n\n### X-Forwarded-For\n\nFor example, if you make this request to your load balancer:\n```bash\ncurl -X POST https://example.com/login -H \"X-Forwarded-For: 1.2.3.4, 11.22.33.44\"\n```\n\nAnd your server behind the load balancer obtain this:\n```\nX-Forwarded-For: 1.2.3.4, 11.22.33.44, \u003cactual client IP\u003e\n```\n\nThat's mean you can't use `X-Forwarded-For` header, because it's **unreliable** and **untrustworthy**.\nSo keep `TrustForwardHeader` disabled in your limiter option.\n\nHowever, if you have configured your reverse proxy to always remove/overwrite `X-Forwarded-For` and/or `X-Real-IP` headers\nso that if you execute this _(same)_ request:\n```bash\ncurl -X POST https://example.com/login -H \"X-Forwarded-For: 1.2.3.4, 11.22.33.44\"\n```\n\nAnd your server behind the load balancer obtain this:\n```\nX-Forwarded-For: \u003cactual client IP\u003e\n```\n\nThen, you can enable `TrustForwardHeader` in your limiter option.\n\n### Custom header\n\nMany CDN and Cloud providers add a custom header to define the client IP. Like for example, this non exhaustive list:\n\n* `Fastly-Client-IP` from Fastly\n* `CF-Connecting-IP` from Cloudflare\n* `X-Azure-ClientIP` from Azure\n\nYou can use these headers using `ClientIPHeader` in your limiter option.\n\n### None of the above\n\nIf none of the above solution are working, please use a custom `KeyGetter` in your middleware.\n\nYou can use this excellent article to help you define the best strategy depending on your network topology and your security need:\nhttps://adam-p.ca/blog/2022/03/x-forwarded-for/\n\nIf you have any idea/suggestions on how we could simplify this steps, don't hesitate to raise an issue.\nWe would like some feedback on how we could implement this steps in the Limiter API.\n\nThank you.\n\n## Why Yet Another Package\n\nYou could ask us: why yet another rate limit package?\n\nBecause existing packages did not suit our needs.\n\nWe tried a lot of alternatives:\n\n1. [Throttled][1]. This package uses the generic cell-rate algorithm. To cite the\n   documentation: _\"The algorithm has been slightly modified from its usual form to\n   support limiting with an additional quantity parameter, such as for limiting the\n   number of bytes uploaded\"_. It is brilliant in term of algorithm but\n   documentation is quite unclear at the moment, we don't need _burst_ feature for\n   now, impossible to get a correct `After-Retry` (when limit exceeds, we can still\n   make a few requests, because of the max burst) and it only supports `http.Handler`\n   middleware (we use [Gin][4]). Currently, we only need to return `429`\n   and `X-Ratelimit-*` headers for `n reqs/duration`.\n\n2. [Speedbump][3]. Good package but maybe too lightweight. No `Reset` support,\n   only one middleware for [Gin][4] framework and too Redis-coupled. We rather\n   prefer to use a \"store\" approach.\n\n3. [Tollbooth][5]. Good one too but does both too much and too little. It limits by\n   remote IP, path, methods, custom headers and basic auth usernames... but does not\n   provide any Redis support (only _in-memory_) and a ready-to-go middleware that sets\n   `X-Ratelimit-*` headers. `tollbooth.LimitByRequest(limiter, r)` only returns an HTTP\n   code.\n\n4. [ratelimit][2]. Probably the closer to our needs but, once again, too\n   lightweight, no middleware available and not active (last commit was in August\n   2014). Some parts of code (Redis) comes from this project. It should deserve much\n   more love.\n\nThere are other many packages on GitHub but most are either too lightweight, too\nold (only support old Go versions) or unmaintained. So that's why we decided to\ncreate yet another one.\n\n## Contributing\n\n- Ping us on twitter:\n  - [@oibafsellig](https://twitter.com/oibafsellig)\n  - [@thoas](https://twitter.com/thoas)\n  - [@novln\\_](https://twitter.com/novln_)\n- Fork the [project](https://github.com/ulule/limiter)\n- Fix [bugs](https://github.com/ulule/limiter/issues)\n\nDon't hesitate ;)\n\n[1]: https://github.com/throttled/throttled\n[2]: https://github.com/r8k/ratelimit\n[3]: https://github.com/etcinit/speedbump\n[4]: https://github.com/gin-gonic/gin\n[5]: https://github.com/didip/tollbooth\n[6]: https://github.com/valyala/fasthttp\n[godoc-url]: https://pkg.go.dev/github.com/ulule/limiter/v3\n[godoc-img]: https://pkg.go.dev/badge/github.com/ulule/limiter/v3\n[license-img]: https://img.shields.io/badge/license-MIT-blue.svg\n[goreport-url]: https://goreportcard.com/report/github.com/ulule/limiter\n[goreport-img]: https://goreportcard.com/badge/github.com/ulule/limiter\n[circle-url]: https://circleci.com/gh/ulule/limiter/tree/master\n[circle-img]: https://circleci.com/gh/ulule/limiter.svg?style=shield\u0026circle-token=baf62ec320dd871b3a4a7e67fa99530fbc877c99\n","funding_links":[],"categories":["开源类库","Web Frameworks","Web框架","XML","Go","Go (134)","Open source library","Actual middlewares","Web 框架","中间件### 中间件","中间件"],"sub_categories":["限流器","Middlewares","中间件","Current Limiter","版本控制`版本控制相关库`","中間件","Fail injection"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fulule%2Flimiter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fulule%2Flimiter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fulule%2Flimiter/lists"}