43
Flask-Limiter Documentation Release 1.4+0.g55df08f.dirty Ali-Akber Saifee Aug 25, 2020

Flask-Limiter Documentation

  • Upload
    others

  • View
    37

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Flask-Limiter Documentation

Flask-Limiter DocumentationRelease 1.4+0.g55df08f.dirty

Ali-Akber Saifee

Aug 25, 2020

Page 2: Flask-Limiter Documentation
Page 3: Flask-Limiter Documentation

CONTENTS

1 Usage 11.1 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Quick start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.3 The Flask-Limiter extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.4 Rate Limit Domain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.5 Decorators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2 Configuration 7

3 Rate limit string notation 93.1 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

4 Rate limiting strategies 114.1 Fixed Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114.2 Fixed Window with Elastic Expiry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114.3 Moving Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

5 Rate-limiting Headers 13

6 Recipes 156.1 Rate Limit Key Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156.2 Custom Rate limit exceeded responses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156.3 Customizing rate limits based on response . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166.4 Using Flask Pluggable Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166.5 Rate limiting all routes in a flask.Blueprint . . . . . . . . . . . . . . . . . . . . . . . . . . . 176.6 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176.7 Custom error messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186.8 Deploying an application behind a proxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

7 API 197.1 Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197.2 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217.3 Utils . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

8 Changelog 238.1 v1.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238.2 v1.3.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238.3 v1.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238.4 v1.2.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248.5 v1.2.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248.6 v1.1.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

i

Page 4: Flask-Limiter Documentation

8.7 v1.0.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248.8 v1.0.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258.9 v0.9.5.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258.10 v0.9.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258.11 v0.9.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258.12 v0.9.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258.13 v0.9.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268.14 v0.9.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268.15 v0.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268.16 v0.8.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268.17 v0.8.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268.18 v0.8.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268.19 v0.8.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278.20 v0.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278.21 v0.7.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278.22 v0.7.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278.23 v0.7.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278.24 v0.7.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278.25 v0.7.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288.26 v0.7.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288.27 v0.7.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288.28 v0.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288.29 v0.6.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288.30 v0.6.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288.31 v0.6.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298.32 v0.6.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298.33 v0.6.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298.34 v0.6.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298.35 v0.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298.36 v0.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298.37 v0.4.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308.38 v0.4.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308.39 v0.4.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308.40 v0.4.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308.41 v0.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308.42 v0.3.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308.43 v0.3.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318.44 v0.3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318.45 v0.2.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318.46 v0.2.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318.47 v0.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318.48 v0.1.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328.49 v0.1.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

9 References 33

10 Contributions 35

Python Module Index 37

Index 39

ii

Page 5: Flask-Limiter Documentation

CHAPTER

ONE

USAGE

1.1 Installation

pip install Flask-Limiter

1.2 Quick start

from flask import Flaskfrom flask_limiter import Limiterfrom flask_limiter.util import get_remote_address

app = Flask(__name__)limiter = Limiter(

app,key_func=get_remote_address,default_limits=["200 per day", "50 per hour"]

)@app.route("/slow")@limiter.limit("1 per day")def slow():

return ":("

@app.route("/medium")@limiter.limit("1/second", override_defaults=False)def medium():

return ":|"

@app.route("/fast")def fast():

return ":)"

@app.route("/ping")@limiter.exemptdef ping():

return "PONG"

The above Flask app will have the following rate limiting characteristics:

• Rate limiting by remote_address of the request

• A default rate limit of 200 per day, and 50 per hour applied to all routes.

1

Page 6: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

• The slow route having an explicit rate limit decorator will bypass the default rate limit and only allow 1 requestper day.

• The medium route inherits the default limits and adds on a decorated limit of 1 request per second.

• The ping route will be exempt from any default rate limits.

Note: The built in flask static files routes are also exempt from rate limits.

Every time a request exceeds the rate limit, the view function will not get called and instead a 429 http error will beraised.

1.3 The Flask-Limiter extension

The extension can be initialized with the flask.Flask application in the usual ways.

Using the constructor

from flask_limiter import Limiterfrom flask_limiter.util import get_remote_address....

limiter = Limiter(app, key_func=get_remote_address)

Deferred app initialization using init_app

limiter = Limiter(key_func=get_remote_address)limiter.init_app(app)

1.4 Rate Limit Domain

Each Limiter instance is initialized with a key_func which returns the bucket in which each request is put into whenevaluating whether it is within the rate limit or not.

Danger: Earlier versions of Flask-Limiter defaulted the rate limiting domain to the requesting users’ ip-addressretreived via the flask_limiter.util.get_ipaddr() function. This behavior is being deprecated (sinceversion 0.9.2) as it can be susceptible to ip spoofing with certain environment setups (more details at github issue#41 & flask apps and ip spoofing).

It is now recommended to explicitly provide a keying function as part of the Limiter initialization (Rate Limit KeyFunctions). Two utility methods are still provided:

• flask_limiter.util.get_ipaddr(): uses the last ip address in the X-Forwarded-For header, else fallsback to the remote_address of the request

• flask_limiter.util.get_remote_address(): uses the remote_address of the request.

Please refer to Deploying an application behind a proxy for an example.

2 Chapter 1. Usage

Page 7: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

1.5 Decorators

The decorators made available as instance methods of the Limiter instance are

Limiter.limit() There are a few ways of using this decorator depending on your preference and use-case.

Single decorator The limit string can be a single limit or a delimiter separated string

@app.route("....")@limiter.limit("100/day;10/hour;1/minute")def my_route()...

Multiple decorators The limit string can be a single limit or a delimiter separated string or a combination ofboth.

@app.route("....")@limiter.limit("100/day")@limiter.limit("10/hour")@limiter.limit("1/minute")def my_route():...

Custom keying function By default rate limits are applied based on the key function that the Limiter in-stance was initialized with. You can implement your own function to retrieve the key to rate limit by whendecorating individual routes. Take a look at Rate Limit Key Functions for some examples..

def my_key_func():...

@app.route("...")@limiter.limit("100/day", my_key_func)def my_route():...

Note: The key function is called from within a flask request context.

Dynamically loaded limit string(s) There may be situations where the rate limits need to be retrieved fromsources external to the code (database, remote api, etc. . . ). This can be achieved by providing a callable tothe decorator.

def rate_limit_from_config():return current_app.config.get("CUSTOM_LIMIT", "10/s")

@app.route("...")@limiter.limit(rate_limit_from_config)def my_route():

...

Danger: The provided callable will be called for every request on the decorated route. Forexpensive retrievals, consider caching the response.

1.5. Decorators 3

Page 8: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

Note: The callable is called from within a flask request context during the before_request phase.

Exemption conditions Each limit can be exempted when given conditions are fulfilled. These conditions canbe specified by supplying a callable as an `exempt_when` argument when defining the limit.

@app.route("/expensive")@limiter.limit("100/day", exempt_when=lambda: current_user.is_admin)def expensive_route():...

Limiter.shared_limit() For scenarios where a rate limit should be shared by multiple routes (For examplewhen you want to protect routes using the same resource with an umbrella rate limit).

Named shared limit

mysql_limit = limiter.shared_limit("100/hour", scope="mysql")

@app.route("..")@mysql_limitdef r1():

...

@app.route("..")@mysql_limitdef r2():

...

Dynamic shared limit: when a callable is passed as scope, the return value of the function will be used as thescope. Note that the callable takes one argument: a string representing the request endpoint.

def host_scope(endpoint_name):return request.host

host_limit = limiter.shared_limit("100/hour", scope=host_scope)

@app.route("..")@host_limitdef r1():

...

@app.route("..")@host_limitdef r2():

...

Note: Shared rate limits provide the same conveniences as individual rate limits

• Can be chained with other shared limits or individual limits

• Accept keying functions

• Accept callables to determine the rate limit value

Limiter.exempt() This decorator simply marks a route as being exempt from any rate limits.

Limiter.request_filter() This decorator simply marks a function as a filter for requests that are going to betested for rate limits. If any of the request filters return True no rate limiting will be performed for that request.This mechanism can be used to create custom white lists.

4 Chapter 1. Usage

Page 9: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

@limiter.request_filterdef header_whitelist():

return request.headers.get("X-Internal", "") == "true"

@limiter.request_filterdef ip_whitelist():

return request.remote_addr == "127.0.0.1"

In the above example, any request that contains the header X-Internal: true or originatesfrom localhost will not be rate limited.

1.5. Decorators 5

Page 10: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

6 Chapter 1. Usage

Page 11: Flask-Limiter Documentation

CHAPTER

TWO

CONFIGURATION

The following flask configuration values are honored by Limiter. If the corresponding configuration value is passedin through the Limiter constructor, those will take precedence.

7

Page 12: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

RATELIMIT_GLOBAL Deprecated since version 0.9.4.: UseRATELIMIT_DEFAULT instead.

RATELIMIT_DEFAULT A comma (or some other delimiter) separated string that willbe used to apply a default limit on all routes. If not provided,the default limits can be passed to the Limiter constructoras well (the values passed to the constructor take precedenceover those in the config). Rate limit string notation for de-tails.

RATELIMIT_DEFAULTS_PER_METHOD Whether default limits are applied per method, per route oras a combination of all method per route.

RATELIMIT_DEFAULTS_EXEMPT_WHEN A function that should return a truthy value if the defaultrate limit(s) should be skipped for the current request. Thiscallback is called in the flask request context before_requestphase.

RATELIMIT_DEFAULTS_DEDUCT_WHEN A function that should return a truthy value if a deductionshould be made from the default rate limit(s) for the currentrequest. This callback is called in the flask request contextafter_request phase.

RATELIMIT_APPLICATION A comma (or some other delimiter) separated string that willbe used to apply limits to the application as a whole (i.e.shared by all routes).

RATELIMIT_STORAGE_URL A storage location conforming to the scheme in Storagescheme. A basic in-memory storage can be used by spec-ifying memory:// though this should probably never beused in production. Some supported backends include:

• Memcached: memcached://host:port• Redis: redis://host:port• GAE Memcached: gaememcached://host:port

For specific examples and requirements of supported back-ends please refer to Storage scheme.

RATELIMIT_STORAGE_OPTIONS A dictionary to set extra options to be passed to the storageimplementation upon initialization. (Useful if you’re sub-classing limits.storage.Storage to create a customStorage backend.)

RATELIMIT_STRATEGY The rate limiting strategy to use. Rate limiting strategies fordetails.

RATELIMIT_HEADERS_ENABLED Enables returning Rate-limiting Headers. Defaults to FalseRATELIMIT_ENABLED Overall kill switch for rate limits. Defaults to TrueRATELIMIT_HEADER_LIMIT Header for the current rate limit. Defaults to

X-RateLimit-LimitRATELIMIT_HEADER_RESET Header for the reset time of the current rate limit. Defaults to

X-RateLimit-ResetRATELIMIT_HEADER_REMAINING Header for the number of requests remaining in the current

rate limit. Defaults to X-RateLimit-RemainingRATELIMIT_HEADER_RETRY_AFTER Header for when the client should retry the request. Defaults

to Retry-AfterRATELIMIT_HEADER_RETRY_AFTER_VALUEAllows configuration of how the value of the Retry-After

header is rendered. One of http-date or delta-seconds.(RFC2616).

RATELIMIT_SWALLOW_ERRORS Whether to allow failures while attempting to perform a ratelimit such as errors with downstream storage. Setting thisvalue to True will effectively disable rate limiting for re-quests where an error has occurred.

RATELIMIT_IN_MEMORY_FALLBACK_ENABLEDTrue/False. If enabled an in memory rate limiterwill be used as a fallback when the configured storageis down. Note that, when used in combination withRATELIMIT_IN_MEMORY_FALLBACK the original ratelimits will not be inherited and the values provided in

RATELIMIT_IN_MEMORY_FALLBACK A comma (or some other delimiter) separated string that willbe used when the configured storage is down.

RATELIMIT_KEY_PREFIX Prefix that is prepended to each stored rate limit key. Thiscan be useful when using a shared storage for multiple appli-cations or rate limit domains.

8 Chapter 2. Configuration

Page 13: Flask-Limiter Documentation

CHAPTER

THREE

RATE LIMIT STRING NOTATION

Rate limits are specified as strings following the format:

[count] [per|/] [n (optional)] [second|minute|hour|day|month|year]

You can combine multiple rate limits by separating them with a delimiter of your choice.

3.1 Examples

• 10 per hour

• 10/hour

• 10/hour;100/day;2000 per year

• 100/day, 500/7days

Warning: If rate limit strings that are provided to the Limiter.limit() decorator are malformed and can’tbe parsed the decorated route will fall back to the default rate limit(s) and an ERROR log message will be emitted.Refer to Logging for more details on capturing this information. Malformed default rate limit strings will howeverraise an exception as they are evaluated early enough to not cause disruption to a running application.

9

Page 14: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

10 Chapter 3. Rate limit string notation

Page 15: Flask-Limiter Documentation

CHAPTER

FOUR

RATE LIMITING STRATEGIES

Flask-Limiter comes with three different rate limiting strategies built-in. Pick the one that works foryour use-case by specifying it in your flask config as RATELIMIT_STRATEGY (one of fixed-window,fixed-window-elastic-expiry, or moving-window), or as a constructor keyword argument. The defaultconfiguration is fixed-window.

4.1 Fixed Window

This is the most memory efficient strategy to use as it maintains one counter per resource and rate limit. It doeshowever have its drawbacks as it allows bursts within each window - thus allowing an ‘attacker’ to by-pass the limits.The effects of these bursts can be partially circumvented by enforcing multiple granularities of windows per resource.

For example, if you specify a 100/minute rate limit on a route, this strategy will allow 100 hits in the last second ofone window and a 100 more in the first second of the next window. To ensure that such bursts are managed, you couldadd a second rate limit of 2/second on the same route.

4.2 Fixed Window with Elastic Expiry

This strategy works almost identically to the Fixed Window strategy with the exception that each hit results in theextension of the window. This strategy works well for creating large penalties for breaching a rate limit.

For example, if you specify a 100/minute rate limit on a route and it is being attacked at the rate of 5 hits per secondfor 2 minutes - the attacker will be locked out of the resource for an extra 60 seconds after the last hit. This strategyhelps circumvent bursts.

4.3 Moving Window

Warning: The moving window strategy is only implemented for the redis and in-memory storage backends.The strategy requires using a list with fast random access which is not very convenient to implement with amemcached storage.

This strategy is the most effective for preventing bursts from by-passing the rate limit as the window for each limit is notfixed at the start and end of each time unit (i.e. N/second for a moving window means N in the last 1000 milliseconds).There is however a higher memory cost associated with this strategy as it requires N items to be maintained in memoryper resource and rate limit.

11

Page 16: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

12 Chapter 4. Rate limiting strategies

Page 17: Flask-Limiter Documentation

CHAPTER

FIVE

RATE-LIMITING HEADERS

If the configuration is enabled, information about the rate limit with respect to the route being requested will be addedto the response headers. Since multiple rate limits can be active for a given route - the rate limit with the lowest timegranularity will be used in the scenario when the request does not breach any rate limits.

X-RateLimit-Limit The total number of requests allowed for the active windowX-RateLimit-Remaining The number of requests remaining in the active window.X-RateLimit-Reset UTC seconds since epoch when the window will be reset.Retry-After Seconds to retry after or the http date when the Rate

Limit will be reset. The way the value is pre-sented depends on the configuration value set in RATE-LIMIT_HEADER_RETRY_AFTER_VALUE and defaults todelta-seconds.

Warning: Enabling the headers has an additional cost with certain storage / strategy combinations.

• Memcached + Fixed Window: an extra key per rate limit is stored to calculate X-RateLimit-Reset

• Redis + Moving Window: an extra call to redis is involved during every request to calculateX-RateLimit-Remaining and X-RateLimit-Reset

The header names can be customised if required by either using the flask configuration (Configuration) values or bysetting the header_mapping property of the Limiter as follows:

from flask_limiter import Limiter, HEADERSlimiter = Limiter()limiter.header_mapping = {

HEADERS.LIMIT : "X-My-Limit",HEADERS.RESET : "X-My-Reset",HEADERS.REMAINING: "X-My-Remaining"

}# or by only partially specifying the overrideslimiter.header_mapping[HEADERS.LIMIT] = 'X-My-Limit'

13

Page 18: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

14 Chapter 5. Rate-limiting Headers

Page 19: Flask-Limiter Documentation

CHAPTER

SIX

RECIPES

6.1 Rate Limit Key Functions

You can easily customize your rate limits to be based on any characteristic of the incoming request. Both the Limiterconstructor and the Limiter.limit() decorator accept a keyword argument key_func that should return a string(or an object that has a string representation).

Rate limiting a route by current user (using Flask-Login):

@route("/test")@[email protected]("1 per day", key_func = lambda : current_user.username)def test_route():

return "42"

Rate limiting all requests by country:

from flask import request, Flaskimport GeoIPgi = GeoIP.open("GeoLiteCity.dat", GeoIP.GEOIP_INDEX_CACHE | GeoIP.GEOIP_CHECK_CACHE)

def get_request_country():return gi.record_by_name(request.remote_addr)['region_name']

app = Flask(__name__)limiter = Limiter(app, default_limits=["10/hour"], key_func = get_request_country)

6.2 Custom Rate limit exceeded responses

The default configuration results in an abort(429) being called every time a rate limit is exceeded for a particularroute. The exceeded limit is added to the response and results in an response body that looks something like:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><title>429 Too Many Requests</title><h1>Too Many Requests</h1><p>1 per 1 day</p>

If you want to configure the response you can register an error handler for the 429 error code in a manner similar tothe following example, which returns a json response instead:

15

Page 20: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

@app.errorhandler(429)def ratelimit_handler(e):

return make_response(jsonify(error="ratelimit exceeded %s" % e.description), 429

)

6.3 Customizing rate limits based on response

For scenarios where the decision to count the current request towards a rate limit can only be made after the re-quest has completed, a callable that accepts the current flask.Response object as its argument can be providedto the Limiter.limit() or Limiter.shared_limit() decorators through the deduct_when keywordarugment. A truthy response from the callable will result in a deduction from the rate limit.

As an example, to only count non 200 responses towards the rate limit

@app.route("..")@limiter.limit(

"1/second",deduct_when=lambda response: response.status_code != 200

)def route():

...

Note: All requests will be tested for the rate limit and rejected accordingly if the rate limit is already hit. Theprovidion of the deduct_when argument only changes whether the request will count towards depleting the rate limit.

6.4 Using Flask Pluggable Views

If you are using a class based approach to defining view function, the regular method of decorating a view function toapply a per route rate limit will not work. You can add rate limits to your view classes using the following approach.

app = Flask(__name__)limiter = Limiter(app, key_func=get_remote_address)

class MyView(flask.views.MethodView):decorators = [limiter.limit("10/second")]def get(self):

return "get"

def put(self):return "put"

Note: This approach is limited to either sharing the same rate limit for all http methods of a given flask.views.View or applying the declared rate limit independently for each http method (to accomplish this, pass in True to theper_method keyword argument to Limiter.limit()). Alternatively, the limit can be restricted to only certainhttp methods by passing them as a list to the methods keyword argument.

16 Chapter 6. Recipes

Page 21: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

The above approach has been tested with sub-classes of flask.views.View, flask.views.MethodViewand flask.ext.restful.Resource.

6.5 Rate limiting all routes in a flask.Blueprint

Limiter.limit(), Limiter.shared_limit() & Limiter.exempt() can all be applied to flask.Blueprint instances as well. In the following example the login Blueprint has a special rate limit applied to all itsroutes, while the help Blueprint is exempt from all rate limits. The regular Blueprint follows the default rate limits.

app = Flask(__name__)login = Blueprint("login", __name__, url_prefix = "/login")regular = Blueprint("regular", __name__, url_prefix = "/regular")doc = Blueprint("doc", __name__, url_prefix = "/doc")

@doc.route("/")def doc_index():

return "doc"

@regular.route("/")def regular_index():

return "regular"

@login.route("/")def login_index():

return "login"

limiter = Limiter(app, default_limits = ["1/second"], key_func=get_remote_→˓address)limiter.limit("60/hour")(login)limiter.exempt(doc)

app.register_blueprint(doc)app.register_blueprint(login)app.register_blueprint(regular)

6.6 Logging

Each Limiter instance has a logger instance variable that is by default not configured with a handler. You canadd your own handler to obtain log messages emitted by flask_limiter.

Simple stdout handler:

limiter = Limiter(app, key_func=get_remote_address)limiter.logger.addHandler(StreamHandler())

Reusing all the handlers of the logger instance of the flask.Flask app:

app = Flask(__name__)limiter = Limiter(app, key_func=get_remote_address)for handler in app.logger.handlers:

limiter.logger.addHandler(handler)

6.5. Rate limiting all routes in a flask.Blueprint 17

Page 22: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

6.7 Custom error messages

Limiter.limit() & Limiter.shared_limit() can be provided with an error_message argument to override the default n per x error message that is returned to the calling client. The error_message argument can either bea simple string or a callable that returns one.

app = Flask(__name__)limiter = Limiter(app, key_func=get_remote_address)

def error_handler():return app.config.get("DEFAULT_ERROR_MESSAGE")

@app.route("/")@limiter.limit("1/second", error_message='chill!')def index():

....

@app.route("/ping")@limiter.limit("10/second", error_message=error_handler)def ping():

....

6.8 Deploying an application behind a proxy

If your application is behind a proxy and you are using werkzeug > 0.9+ you can use the werkzeug.contrib.fixers.ProxyFix fixer to reliably get the remote address of the user, while protecting your application against ipspoofing via headers.

from flask import Flaskfrom flask_limiter import Limiterfrom flask_limiter.util import get_remote_addressfrom werkzeug.contrib.fixers import ProxyFix

app = Flask(__name__)# for example if the request goes through one proxy# before hitting your application serverapp.wsgi_app = ProxyFix(app.wsgi_app, num_proxies=1)limiter = Limiter(app, key_func=get_remote_address)

18 Chapter 6. Recipes

Page 23: Flask-Limiter Documentation

CHAPTER

SEVEN

API

7.1 Core

class flask_limiter.Limiter(app=None, key_func=None, global_limits=[], default_limits=[], de-fault_limits_per_method=False, default_limits_exempt_when=None,default_limits_deduct_when=None, application_limits=[], head-ers_enabled=False, strategy=None, storage_uri=None, stor-age_options={}, auto_check=True, swallow_errors=False,in_memory_fallback=[], in_memory_fallback_enabled=False,retry_after=None, key_prefix='', enabled=True)

Bases: object

The Limiter class initializes the Flask-Limiter extension.

Parameters

• app – flask.Flask instance to initialize the extension with.

• default_limits (list) – a variable list of strings or callables returning strings denot-ing global limits to apply to all routes. Rate limit string notation for more details.

• default_limits_per_method (bool) – whether default limits are applied permethod, per route or as a combination of all method per route.

• default_limits_exempt_when (function) – a function that should returnTrue/False to decide if the default limits should be skipped

• default_limits_deduct_when (function) – a function that receives the currentflask.Response object and returns True/False to decide if a deduction should be madefrom the default rate limit(s)

• application_limits (list) – a variable list of strings or callables returning stringsfor limits that are applied to the entire application (i.e a shared limit for all routes)

• key_func (function) – a callable that returns the domain to rate limit by.

• headers_enabled (bool) – whether X-RateLimit response headers are written.

• strategy (str) – the strategy to use. Refer to Rate limiting strategies

• storage_uri (str) – the storage location. Refer to Configuration

• storage_options (dict) – kwargs to pass to the storage implementation upon instan-tiation.

• auto_check (bool) – whether to automatically check the rate limit in the before_requestchain of the application. default True

19

Page 24: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

• swallow_errors (bool) – whether to swallow errors when hitting a rate limit. Anexception will still be logged. default False

• in_memory_fallback (list) – a variable list of strings or callables returning stringsdenoting fallback limits to apply when the storage is down.

• in_memory_fallback_enabled (bool) – simply falls back to in memory storagewhen the main storage is down and inherits the original limits.

• retry_after (str) – Allows configuration of how the value of the Retry-After headeris rendered. One of http-date or delta-seconds.

• key_prefix (str) – prefix prepended to rate limiter keys.

check()check the limits for the current request

Raises RateLimitExceeded

exempt(obj)decorator to mark a view or all views in a blueprint as exempt from rate limits.

init_app(app)

Parameters app – flask.Flask instance to rate limit.

limit(limit_value, key_func=None, per_method=False, methods=None, error_message=None, ex-empt_when=None, override_defaults=True, deduct_when=None)

decorator to be used for rate limiting individual routes or blueprints.

Parameters

• limit_value – rate limit string or a callable that returns a string. Rate limit stringnotation for more details.

• key_func (function) – function/lambda to extract the unique identifier for the ratelimit. defaults to remote address of the request.

• per_method (bool) – whether the limit is sub categorized into the http method of therequest.

• methods (list) – if specified, only the methods in this list will be rate limited (default:None).

• error_message – string (or callable that returns one) to override the error messageused in the response.

• exempt_when (function) – function/lambda used to decide if the rate limit shouldskipped.

• override_defaults (bool) – whether the decorated limit overrides the default lim-its. (default: True)

• deduct_when (function) – a function that receives the current flask.Responseobject and returns True/False to decide if a deduction should be done from the rate limit

request_filter(fn)decorator to mark a function as a filter to be executed to check if the request is exempt from rate limiting.

reset()resets the storage if it supports being reset

shared_limit(limit_value, scope, key_func=None, error_message=None, exempt_when=None, over-ride_defaults=True, deduct_when=None)

decorator to be applied to multiple routes sharing the same rate limit.

20 Chapter 7. API

Page 25: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

Parameters

• limit_value – rate limit string or a callable that returns a string. Rate limit stringnotation for more details.

• scope – a string or callable that returns a string for defining the rate limiting scope.

• key_func (function) – function/lambda to extract the unique identifier for the ratelimit. defaults to remote address of the request.

• error_message – string (or callable that returns one) to override the error messageused in the response.

• exempt_when (function) – function/lambda used to decide if the rate limit shouldskipped.

• override_defaults (bool) – whether the decorated limit overrides the default lim-its. (default: True)

• deduct_when (function) – a function that receives the current flask.Responseobject and returns True/False to decide if a deduction should be done from the rate limit

7.2 Exceptions

exception flask_limiter.RateLimitExceeded(limit)Bases: werkzeug.exceptions.TooManyRequests

exception raised when a rate limit is hit.

The exception results in abort(429) being called.

7.3 Utils

flask_limiter.util.get_ipaddr()

Returns the ip address for the current request (or 127.0.0.1 if none found) based on the X-Forwarded-For headers.

Deprecated since version 0.9.2.

flask_limiter.util.get_remote_address()

Returns the ip address for the current request (or 127.0.0.1 if none found)

7.2. Exceptions 21

Page 26: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

22 Chapter 7. API

Page 27: Flask-Limiter Documentation

CHAPTER

EIGHT

CHANGELOG

8.1 v1.4

Release Date: 2020-08-25

• Bug Fix

– Always set headers for conditional limits

– Skip init_app sequence when the rate limiter is disabled

8.2 v1.3.1

Release Date: 2020-05-21

• Bug Fix

– Ensure headers provided explictely by setting _header_mapping take precedence over configuration val-ues.

8.3 v1.3

Release Date: 2020-05-20

• Features

– Add new deduct_when argument that accepts a function to decorated limits to conditionally performdepletion of a rate limit (Pull Request 248)

– Add new default_limits_deduct_when argument to Limiter constructor to conditionally performdepletion of default rate limits

– Add default_limits_exempt_when argument that accepts a function to allow skipping the defaultlimits in the before_request phase

• Bug Fix

– Fix handling of storage failures during after_request phase.

• Code Quality

– Use github-actions instead of travis for CI

– Use pytest instaad of nosetests

23

Page 28: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

– Add docker configuration for test dependencies

– Increase code coverage to 100%

– Ensure pyflake8 compliance

8.4 v1.2.1

Release Date: 2020-02-26

• Bug fix

– Syntax error in version 1.2.0 when application limits are provided through configuration file (Issue 241)

8.5 v1.2.0

Release Date: 2020-02-25

• Add override_defaults argument to decorated limits to allow combinined defaults with decorated limits.

• Add configuration parameter RATELIMIT_DEFAULTS_PER_METHOD to control whether defaults are ap-plied per method.

• Add support for in memory fallback without override (Pull Request 236)

• Bug fix

– Ensure defaults are enforced when decorated limits are skipped (Issue 238)

8.6 v1.1.0

Release Date: 2019-10-02

• Provide Rate limit information with Exception (Pull Request 202)

• Respect existing Retry-After header values (Pull Request 143)

• Documentation improvements

8.7 v1.0.1

Release Date: 2017-12-08

• Bug fix

– Duplicate rate limits applied via application limits (Issue 108)

24 Chapter 8. Changelog

Page 29: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

8.8 v1.0.0

Release Date: 2017-11-06

• Improved documentation for handling ip addresses for applications behind proxiues (Issue 41)

• Execute rate limits for decorated routes in decorator instead of before_request (Issue 67)

• Bug Fix

– Python 3.5 Errors (Issue 82)

– RATELIMIT_KEY_PREFIX configuration constant not used (Issue 88)

– Can’t use dynamic limit in default_limits (Issue 94)

– Retry-After header always zero when using key prefix (Issue 99)

8.9 v0.9.5.1

Release Date: 2017-08-18

• Upgrade versioneer

8.10 v0.9.5

Release Date: 2017-07-26

• Add support for key prefixes

8.11 v0.9.4

Release Date: 2017-05-01

• Implemented application wide shared limits

8.12 v0.9.3

Release Date: 2016-03-14

• Allow reset of limiter storage if available

8.8. v1.0.0 25

Page 30: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

8.13 v0.9.2

Release Date: 2016-03-04

• Deprecation warning for default key_func get_ipaddr

• Support for Retry-After header

8.14 v0.9.1

Release Date: 2015-11-21

• Re-expose enabled property on Limiter instance.

8.15 v0.9

Release Date: 2015-11-13

• In-memory fallback option for unresponsive storage

• Rate limit exemption option per limit

8.16 v0.8.5

Release Date: 2015-10-05

• Bug fix for reported issues of missing (limits) dependency upon installation.

8.17 v0.8.4

Release Date: 2015-10-03

• Documentation tweaks.

8.18 v0.8.2

Release Date: 2015-09-17

• Remove outdated files from egg

26 Chapter 8. Changelog

Page 31: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

8.19 v0.8.1

Release Date: 2015-08-06

• Fixed compatibility with latest version of Flask-Restful

8.20 v0.8

Release Date: 2015-06-07

• No functional change

8.21 v0.7.9

Release Date: 2015-04-02

• Bug fix for case sensitive methods whitelist for limits decorator

8.22 v0.7.8

Release Date: 2015-03-20

• Hotfix for dynamic limits with blueprints

• Undocumented feature to pass storage options to underlying storage backend.

8.23 v0.7.6

Release Date: 2015-03-02

• methods keyword argument for limits decorator to specify specific http methods to apply the rate limit to.

8.24 v0.7.5

Release Date: 2015-02-16

• Custom error messages.

8.19. v0.8.1 27

Page 32: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

8.25 v0.7.4

Release Date: 2015-02-03

• Use Werkzeug TooManyRequests as the exception raised when available.

8.26 v0.7.3

Release Date: 2015-01-30

• Bug Fix

– Fix for version comparison when monkey patching Werkzeug (Issue 24)

8.27 v0.7.1

Release Date: 2015-01-09

• Refactor core storage & ratelimiting strategy out into the limits package.

• Remove duplicate hits when stacked rate limits are in use and a rate limit is hit.

8.28 v0.7

Release Date: 2015-01-09

• Refactoring of RedisStorage for extensibility (Issue 18)

• Bug fix: Correct default setting for enabling rate limit headers. (Issue 22)

8.29 v0.6.6

Release Date: 2014-10-21

• Bug fix

– Fix for responses slower than rate limiting window. (Issue 17.)

8.30 v0.6.5

Release Date: 2014-10-01

• Bug fix: in memory storage thread safety

28 Chapter 8. Changelog

Page 33: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

8.31 v0.6.4

Release Date: 2014-08-31

• Support for manually triggering rate limit check

8.32 v0.6.3

Release Date: 2014-08-26

• Header name overrides

8.33 v0.6.2

Release Date: 2014-07-13

• Rate limiting for blueprints

8.34 v0.6.1

Release Date: 2014-07-11

• per http method rate limit separation (Recipe)

• documentation improvements

8.35 v0.6

Release Date: 2014-06-24

• Shared limits between routes

8.36 v0.5

Release Date: 2014-06-13

• Request Filters

8.31. v0.6.4 29

Page 34: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

8.37 v0.4.4

Release Date: 2014-06-13

• Bug fix

– Werkzeug < 0.9 Compatibility (Issue 6.)

8.38 v0.4.3

Release Date: 2014-06-12

• Hotfix : use HTTPException instead of abort to play well with other extensions.

8.39 v0.4.2

Release Date: 2014-06-12

• Allow configuration overrides via extension constructor

8.40 v0.4.1

Release Date: 2014-06-04

• Improved implementation of moving-window X-RateLimit-Reset value.

8.41 v0.4

Release Date: 2014-05-28

• Rate limiting headers

8.42 v0.3.2

Release Date: 2014-05-26

• Bug fix

– Memory leak when using Limiter.storage.MemoryStorage (Issue 4.)

• Improved test coverage

30 Chapter 8. Changelog

Page 35: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

8.43 v0.3.1

Release Date: 2014-02-20

• Strict version requirement on six

• documentation tweaks

8.44 v0.3.0

Release Date: 2014-02-19

• improved logging support for multiple handlers

• allow callables to be passed to Limiter.limit decorator to dynamically load rate limit strings.

• add a global kill switch in flask config for all rate limits.

• Bug fixes

– default key function for rate limit domain wasn’t accounting for X-Forwarded-For header.

8.45 v0.2.2

Release Date: 2014-02-18

• add new decorator to exempt routes from limiting.

• Bug fixes

– versioneer.py wasn’t included in manifest.

– configuration string for strategy was out of sync with docs.

8.46 v0.2.1

Release Date: 2014-02-15

• python 2.6 support via counter backport

• source docs.

8.47 v0.2

Release Date: 2014-02-15

• Implemented configurable strategies for rate limiting.

• Bug fixes

– better locking for in-memory storage

– multi threading support for memcached storage

8.43. v0.3.1 31

Page 36: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

8.48 v0.1.1

Release Date: 2014-02-14

• Bug fixes

– fix initializing the extension without an app

– don’t rate limit static files

8.49 v0.1.0

Release Date: 2014-02-13

• first release.

32 Chapter 8. Changelog

Page 37: Flask-Limiter Documentation

CHAPTER

NINE

REFERENCES

• Redis rate limiting pattern #2

• DomainTools redis rate limiter

• limits: python rate limiting utilities

33

Page 38: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

34 Chapter 9. References

Page 39: Flask-Limiter Documentation

CHAPTER

TEN

CONTRIBUTIONS

• Timothee Groleau

• Zehua Liu

• Guilherme Polo

• Mattias Granlund

• Josh Friend

• Sami Hiltunen

• Henning Peters

35

Page 40: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

36 Chapter 10. Contributions

Page 41: Flask-Limiter Documentation

PYTHON MODULE INDEX

fflask_limiter.util, 21

37

Page 42: Flask-Limiter Documentation

Flask-Limiter Documentation, Release 1.4+0.g55df08f.dirty

38 Python Module Index

Page 43: Flask-Limiter Documentation

INDEX

Ccheck() (flask_limiter.Limiter method), 20

Eexempt() (flask_limiter.Limiter method), 20

Fflask_limiter.util

module, 21

Gget_ipaddr() (in module flask_limiter.util), 21get_remote_address() (in module

flask_limiter.util), 21

Iinit_app() (flask_limiter.Limiter method), 20

Llimit() (flask_limiter.Limiter method), 20Limiter (class in flask_limiter), 19

Mmodule

flask_limiter.util, 21

RRateLimitExceeded, 21request_filter() (flask_limiter.Limiter method),

20reset() (flask_limiter.Limiter method), 20

Sshared_limit() (flask_limiter.Limiter method), 20

39