Rate Limiting extension for Flask

Related tags

Flaskflask-limiter
Overview

Flask-Limiter

docs ci codecov pypi license

Flask-Limiter provides rate limiting features to flask routes. It has support for a configurable backend for storage with current implementations for in-memory, redis and memcache.

Quickstart

Add the rate limiter to your flask app. The following example uses the default in memory implementation for storage.

from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)
limiter = Limiter(
    app,
    key_func=get_remote_address,
    default_limits=["2 per minute", "1 per second"],
)

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

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

@app.route("/ping")
@limiter.exempt
def ping():
    return 'PONG'

app.run()

Test it out. The fast endpoint respects the default rate limit while the slow endpoint uses the decorated one. ping has no rate limit associated with it.

$ curl localhost:5000/fast
42
$ curl localhost:5000/fast
42
$ curl localhost:5000/fast
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>429 Too Many Requests</title>
<h1>Too Many Requests</h1>
<p>2 per 1 minute</p>
$ curl localhost:5000/slow
24
$ curl localhost:5000/slow
<!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>
$ curl localhost:5000/ping
PONG
$ curl localhost:5000/ping
PONG
$ curl localhost:5000/ping
PONG
$ curl localhost:5000/ping
PONG

Read the docs

Comments
  • Issue at Litespeed server and Python 3.9.12

    Issue at Litespeed server and Python 3.9.12

    Hi,

    I am facing an intermittent issue with flask-limiter running on Python 3.9.12 on a Litespeed server.

    Sometimes the web page loads, but most of the time, does not load. You can see the attached images... Loaded in the first access (counter = 1), but in the second time, after a page reload (at browser), it keep trying to load, and after some time returns "Request Timeout" error.

    Here is the full python code:

    from flask import Flask, request
    from flask_limiter import Limiter
    from flask_limiter.util import get_remote_address
    
    app = Flask(__name__)
    limiter = Limiter(app, key_func=get_remote_address, default_limits=['300/day'], enabled=True)
    
    
    counter = 0
    @app.route('/')
    @limiter.limit('200 per day')
    @limiter.limit('50 per hour')
    @limiter.limit('10 per minute')
    def hello_world():
        global counter
        counter = counter + 1
        return f'Hello World! Visit number: {counter}'
    
    
    if __name__ == '__main__':
        app.run()
    
    

    If I set enabled=False then the problem disappears: limiter = Limiter(app, key_func=get_remote_address, default_limits=['300/day'], enabled=False)

    My best guess is some incompatibility with Litespeed server and/or its native WSGI. Any idea on how to fix?

    flask-liimiter-problem flask-liimiter-problem-2 .

    opened by sukhoi47 20
  • Limit decorator for non standard routing (Flask-Restful)

    Limit decorator for non standard routing (Flask-Restful)

    Current situation: When the decorator is loaded, a route is added to route_limits or dynamic_route_limits. The name of the route is defined like this:

    name = "%s.%s" % (fn.__module__, fn.__name__)
    

    Then for every 'before request', __check_request_limit is called. This function tries to find the correct name for the end point and see if there is a limit defined for it in either dictionaries:

    endpoint = request.endpoint or ""
            view_func = current_app.view_functions.get(endpoint, None)
            name = ("%s.%s" % (
                    view_func.__module__, view_func.__name__
                ) if view_func else ""
    

    This works well for standard routing. But now I want to use Flask-Limiter for Flask-Restful routes. And this creates a problem because the endpoint name when checking the limit will be different than the name that is determined when the decorator is defined.

    For example, module myapp.api:

    class HelloWorld(restful.Resource):
        @limiter.limit("1/minute")
        def get(self):
            return {'hello': 'world'}
    
    api.add_resource(HelloWorld, '/')
    

    would create something like myapp.api.get as name for the limiter route, but is actually defined as myapp.api.helloworld by Flask-Restful. Of course this is not specific to Flask-Restful, and can occur any time the limiter is used on a function that is not also the actual endpoint, I think.

    Any thoughts on how to fix this in a nice way? Maybe the checking of the rate limiting could be better done directly in the decorator instead of 1 central before_request?

    enhancement question 
    opened by marijns 20
  • Share limits between several routes

    Share limits between several routes

    It looks like the current implementation does not allow sharing a single custom rate limit among several routes (global limits excluded).

    For instance, take the example where you have a /slow route limited to 1 request per day. What if I have a second route which I would like to share the limit of 1 request per day along the other /slow route? Right now the code will limit each route to 1 req per day, while it would be useful to allow 1 req per day for all the slow routes combined.

    enhancement 
    opened by g-p-g 17
  • Retry-After header reporting some optimistic, and occasionally strange values

    Retry-After header reporting some optimistic, and occasionally strange values

    Hi,

    I am having a slight issue with the Retry-After headers on my current project. If the user triggers too many requests to my rate limited endpoint, I am using the Retry-After header to wait for the right amount of time before letting their request through.

    However, it almost seems that the header is a little "optimistic" timewise, because if I retry after that exact number of seconds, it triggers the rate limit again. However, on this request, the rate limiter actually says Retry-After zero seconds. It says this several times in quick succession, before then saying Retry-After -1484742195 seconds (exact value varies, but it's always large and negative).

    I don't know whether I've misunderstood something, or if I'm doing something else daft, but I wondered how this value was calculated, and how reliable it was? If I arbitrarily add a few seconds to the Retry-After time it works fine, but I am reluctant to add a fixed value such as 5 seconds in case there are times when 5 seconds aren't enough. Adding 1 second doesn't seem to be sufficient, but adding 2 seconds seems to do the trick.

    Once that Retry-After is up, what actually happens internally? Is it simply that I am observing the rate limit resetting itself and taking a non-zero amount of time to do so?

    Thanks!

    opened by andymantell 15
  • Is it possible to abort with a custom exception (no 429 code)?

    Is it possible to abort with a custom exception (no 429 code)?

    The doc says : "The default configuration results in an abort(429) being called [...]".

    Is it possible to customize this behaviour, for example:

    • aborting with an other HTTP code
    • raising a custom exception
    • only logging the remote ip address
    opened by Horace89 13
  • Bundle decorator to run the check

    Bundle decorator to run the check

    I have disabled auto_check because I want to trigger rate-limit check at a specific point during request processing.

    My routes are something like this:

    @app.route(...)
    @auth.auth(...)
    ...
    def route():
        ...
    

    And I want limiter to run after auth check. So I added something like:

    class ExtLimiter(Limiter):
    
        def rate_limit(self):
            def wrapper(f):
                @functools.wraps(f)
                def wrapped(*args, **kwargs):
                    self.check()
                    return f(*args, **kwargs)
                return wrapped
            return wrapper
    

    And now I:

    @app.route(...)
    @auth.auth(...)
    @limiter.rate_limit()
    ...
    def route():
        ...
    

    I think this simple decorator could be bundled in Flask-Limiter?

    (I was kinda surprised when @limiter.limit() only registered the route and did not run the check() at all. So, added this decorator.)

    enhancement 
    opened by tuukkamustonen 13
  • Requirements missing

    Requirements missing

    I installed the package of version 0.8.1 using pip install and the package was unable to run since the limits package was missing. I just added manually the limits package to my requirements.

    Can you fix it please...

    opened by AlmogCohen 13
  • Requests that have variable cost?

    Requests that have variable cost?

    Hey guys, I'm curious whether flask-limiter could help when requests have variable cost. For instance, can I count a single request against the limit with some multiplier based on the content of the request?

    If not, do you all have any thoughts on how to help with this type or metering?

    enhancement 
    opened by milescrawford 12
  • unable to disable limiter during testing

    unable to disable limiter during testing

    How can we disable the rate limiter during our unit testing? I cannot get it to work. Setting RATELIMIT_ENABLED = False doesn't seem to work. I'd like to globally turn off the entire limiter since I use the limiter.limit() in a lot of different locations, but don't want to add testing conditions everywhere. Here is my app

    def create_app(object_config=ProdConfig):
        app = Flask(__name__)
        app.config.from_object(object_config)
        ...
        limiter.init_app(app)
        for handler in app.logger.handlers:
            limiter.logger.addHandler(handler)
    
        return app    
    

    and here is my testing app inside my pytest conftest.py

    from myapp import create_app
    import pytest
    
    @pytest.fixture(scope='session')
    def app():
        app = create_app(debug=True, local=True, object_config=TestConfig)
        return app
    

    I load it with a Test Config which has RATELIMIT_ENABLED set to False but it does not work

    class TestConfig(Config):
        TESTING = True
        DEBUG = True
        SQLALCHEMY_DATABASE_URI = 'sqlite://'
        BCRYPT_LOG_ROUNDS = 1  # For faster tests
        WTF_CSRF_ENABLED = False  # Allows form testing
        PRESERVE_CONTEXT_ON_EXCEPTION = False
        USE_PROFILER = False  # Turn off the Flask Profiler extension
        RATELIMIT_ENABLED = False  # Turn off the Flask Rate Limiter
    
    
    
    question 
    opened by havok2063 12
  • Possible to fall back to in-memory storage?

    Possible to fall back to in-memory storage?

    Is it possible currently to have it fall back to an in-memory storage scheme if the redis/memcached server was down? I didn't see anything in the code, so I assume the answer is "no". But just wanted to ask.

    I would assume this would be opt-in, not everyone would want this behavior. But I see a huge benefit to having the rate limiting just flip back to in-memory if the remote server was down.

    If the flask app was under high load, the request load could take down a memcache/redis server. So having it flip to in-memory means an endpoint could stay up and then the in-memory rate limit would get exceeded on each server and the offending client would get blocked (just would take longer as they'd have to hit limits on each individual server). While other clients would still get access.

    enhancement 
    opened by jonathanq 12
  • Q: Can we use multiple Limiters on a single app?

    Q: Can we use multiple Limiters on a single app?

    Hi alisaifee,

    I am trying to apply two sets of default limits on all routes, by using this code:

    limiter = Limiter(app,
        key_func=get_remote_address,
        default_limits=["30 / minute", "500 / hour", "2000 / day"],
        headers_enabled=True,
        storage_uri=LIMITER_URI,
        in_memory_fallback_enabled=True
    )
    global_limiter = Limiter(
        app,
        key_func=lambda:"global_bucket",
        default_limits=["100 / minute"],
        headers_enabled=True,
        storage_uri=LIMITER_URI,
        in_memory_fallback_enabled=True
    )
    

    This behaves differently than using global_limiter = limiter.shared_limit("100/minute", scope=lambda:"global_bucket") because this way, I wouldn't have to mention @global_limiterbefore all routes.

    However it appears global_limiter is not applied.

    Besides, I could not find any example of people using multiple Limiters on any app. Would you have an idea of how to accomplish this?

    enhancement 
    opened by Lalbatros 11
  • Cannot integrate with Flask-Application builder as viewfunc(endpoint) !=

    Cannot integrate with Flask-Application builder as viewfunc(endpoint) != "{obj.__module__}.{obj.__name__}"

    I am trying to add support for rate limiting in Flask-Application-Builder (FAB). This means that the limiter is instantiated as part of appbuilder initalizing and will be maintained in app.appbuilder.sm.limiter (sm is the security manager of FAB).

    FAB has several ways to provide logins. I tried decorating the login views as follows

    class AuthDBView(AuthView):
        login_template = "appbuilder/general/security/login_db.html"
    
        @expose("/login/", methods=["GET", "POST"])
        def login(self):
            @self.appbuilder.sm.limiter.limit(limit_value="2 per 5 seconds")
            def _login(self):
                if g.user is not None and g.user.is_authenticated:
                    return redirect(self.appbuilder.get_url_for_index)
                form = LoginForm_db()
                if form.validate_on_submit():
                    user = self.appbuilder.sm.auth_user_db(
                        form.username.data, form.password.data
                    )
                    if not user:
                        flash(as_unicode(self.invalid_login_message), "warning")
                        return redirect(self.appbuilder.get_url_for_login)
                    login_user(user, remember=False)
                    next_url = request.args.get("next", "")
                    return redirect(get_safe_redirect(next_url))
                return self.render_template(
                    self.login_template, title=self.title, form=form, appbuilder=self.appbuilder
                )
    
            return _login(self)
    

    This doesn't work. This is due to:

    name = f"{obj.__module__}.{obj.__name__}" evaluates to name being AuthDBView.login and in view_func = app.view_functions.get(endpoint, None) evaluates to flask_appbuilder.security.views.login (after __module__.__name__). This prevents lookups from working.

    If I set name = endpoint in route_limits and do marked_for_limiting = name in self._marked_for_limiting or endpoint in self._marked_for_limiting things start working.

    I can provide a PR for this, but I am not sure of this is the right approach :-)

    opened by bolkedebruin 7
Releases(3.1.0)
a flask profiler which watches endpoint calls and tries to make some analysis.

Flask-profiler version: 1.8 Flask-profiler measures endpoints defined in your flask application; and provides you fine-grained report through a web in

Mustafa Atik 718 Dec 20, 2022
An flask app for fake image detector

fake_img_detector This is a ml based project: frameworks used:- Flask Google collab #Description: Here you can Upload two different looking image with

shivam kumar 7 Jun 29, 2022
Flask webassets integration.

Integrates the webassets library with Flask, adding support for merging, minifying and compiling CSS and Javascript files. Documentation: https://flas

Michael Elsdörfer 433 Dec 29, 2022
Python3🐍 webApp to display your current playing music on OBS Studio.

Spotify Overlay A Overlay to display on Obs Studio or any related video/stream recorder, the current music that is playing on your Spotify. Installati

carlitos 0 Oct 17, 2022
Brandnew-flask is a CLI tool used to generate a powerful and mordern flask-app that supports the production environment.

Brandnew-flask is still in the initial stage and needs to be updated and improved continuously. Everyone is welcome to maintain and improve this CLI.

brandonye 4 Jul 17, 2022
Flask Boilerplate - Paper Kit Design | AppSeed

Flask Paper Kit Open-Source Web App coded in Flask Framework - Provided by AppSeed Web App Generator. App Features: SQLite database SQLAlchemy ORM Ses

App Generator 86 Nov 29, 2021
Geometry Dash Song Bypass with Python Flask Server

Geometry Dash Song Bypass with Python Flask Server

pixelsuft‮ 1 Nov 16, 2021
Live Corona statistics and information site with flask.

Flask Live Corona Info Live Corona statistics and information site with flask. Tools Flask Scrapy Matplotlib How to Run Project Download Codes git clo

Mohammad Dori 5 Jul 15, 2022
Flask Web DRY full-stack framework by Problem Fighter

In the name of God, the Most Gracious, the Most Merciful. PF-Flask-Web Documentation Install and update using pip: pip install -U PF-Flask-Web Please

Problem Fighter 2 Jan 20, 2022
Force SSL on your Flask app.

Flask-SSLify This is a simple Flask extension that configures your Flask application to redirect all incoming requests to HTTPS. The extension is no l

Kenneth Reitz 26 Dec 07, 2022
A template for Flask APIs.

FlaskAPITempate A template for a Flask API. Why tho? I just wanted an easy way to create a Flask API. How to setup First, use the template. You can do

TechStudent10 1 Dec 28, 2021
A gRpc server like Flask (像Flask一样的gRpc服务)

Mask A gRpc server just like Flask. Install Mask support pypi packages, you can simply install by: pip install mask Document Mask manual could be fou

吴东 16 Jun 14, 2022
Flask Apps - Open-Source And Paid | AppSeed

Flask Apps Open-Source web apps built with automation tools, HTML parsing and boilerplated code in Flask - - Provided by AppSeed App Generator. What i

App Generator 120 Oct 04, 2022
Pf-flask-rest-com - Flask REST API Common Implementation by Problem Fighter Library

In the name of God, the Most Gracious, the Most Merciful. PF-Flask-Rest-Com Docu

Problem Fighter 3 Jan 15, 2022
Free casino website. Madden just for learning / fun

Website Casino Free casino website. Madden just for learning / fun. Uses Jinja2 (HTML), Flask, JavaScript, etc. Dice game Preview

Kirill Zhosul 0 Jun 22, 2022
An Instagram Clone using Flask, Python, Redux, Thunk, React

An Instagram Clone using Flask, Python, Redux, Thunk, React

1 Dec 09, 2021
A service made with Flask and Python to help you find the weather of your favorite cities.

Weather-App A service made with Flask and Python to help you find the weather of your favorite cities. Features Backend using Flask and Jinja Weather

Cauã Rinaldi 1 Nov 17, 2022
Flask 文档中文翻译

Flask 中文文档 这里是 Flask 文档中文翻译项目,欢迎参与! 在开始翻译之前,请务必阅读下面的 Contributing Guide 了解贡献流程,然后阅读这个 Issue 了解翻译要求,在这个 Discussion 投票选出你认为合适的翻译词汇,在这个 Discussion 投票选出你喜

Grey Li 93 Nov 28, 2022
A Flask extension that enables or disables features based on configuration.

Flask FeatureFlags This is a Flask extension that adds feature flagging to your applications. This lets you turn parts of your site on or off based on

Rachel Greenfield 131 Sep 26, 2022
Pagination support for flask

flask-paginate Pagination support for flask framework (study from will_paginate). It supports several css frameworks. It requires Python2.6+ as string

Lix Xu 264 Nov 07, 2022