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 web application that consists of a collection of board games

PyBoardGame About This website contains a collection of board games for users to enjoy, as well as various guides for the games. The web app is built

Larry Shi 0 Aug 11, 2021
A simple application builder. Made with python.

Python Flask Server Template Check the Github Repository for updates Flask is an application builder. It is very common in Python but can also be used

1 Jan 09, 2022
Flask starter template for better structuring.

Flask Starter app Flask starter template for better structuring. use the starter plate step 1 : cloning this repo through git clone the repo git clone

Tirtharaj Sinha 1 Jul 26, 2022
A clean and simple blog system based on Flask and MongoDB

CleanBlog A clean and simple blog system based on Flask and MongoDB You can access CleanBlog This is the source code of Flask Tutorial Pro,you can buy

shin 107 Oct 06, 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
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
Curso Desenvolvimento avançado Python com Flask e REST API

Curso Desenvolvimento avançado Python com Flask e REST API Curso da Digital Innovation One Professor: Rafael Galleani Conteudo do curso Introdução ao

Elizeu Barbosa Abreu 1 Nov 14, 2021
This is a simple web application using Python Flask and MySQL database.

Simple Web Application This is a simple web application using Python Flask and MySQL database. This is used in the demonstration of development of Ans

Alaaddin Tarhan 1 Nov 16, 2021
A fairly common feature in web applications to have links that open a popover when hovered

Add Popovers to Links in Flask App It is a fairly common feature in web applications to have links that open a popover when hovered. Twitter does this

Gitau Harrison 1 Jan 22, 2022
Glauth management ui created with python/flask

glauth-ui Glauth-UI is a small flask web app i created to manage the minimal glauth ldap server. I created this as i wanted to use glauth for authenti

Nils Thiele 67 Nov 29, 2022
This is a Flask web app which predicts fare of Flight ticket

Flight Fare Prediction: Table of Content Demo Overview Motivation Installation Deployement on Heroku Directory Tree Bug / Feature Request Future scope

Ayshwarya 1 Jan 24, 2022
Flask-Bcrypt is a Flask extension that provides bcrypt hashing utilities for your application.

Flask-Bcrypt Flask-Bcrypt is a Flask extension that provides bcrypt hashing utilities for your application. Due to the recent increased prevelance of

Max Countryman 310 Dec 14, 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
A Flask wrapper of Starknet state. Similar in purpose to Ganache.

Introduction A Flask wrapper of Starknet state. Similar in purpose to Ganache. Aims to mimic Starknet's Alpha testnet, but with simplified functionali

Shard Labs 159 Jan 04, 2023
Rich implementation for Flask

Flask Rich Implements the Rich programming library with Flask. All features are toggleable, including: Better logging Colorful tracebacks Usage Import

BD103 13 Jun 06, 2022
A Cyberland server written in Python with Flask.

Cyberland What is Cyberland Cyberland is a textboard that offers no frontend. Most of the time, the user makes their own front end. The protocol, as f

Maxime Bouillot 9 Nov 26, 2022
iloveflask is a Python library to collect functions that help a flask developer generate reports, config files and repeat code.

I Love Flask iloveflask is a Python library to collect functions that help a flask developer generate reports, config files and repeat code. Installat

2 Dec 29, 2021
WebSocket support for Flask

flask-sock WebSocket support for Flask Installation pip install flask-sock Example from flask import Flask, render_template from flask_sock import Soc

Miguel Grinberg 165 Dec 27, 2022
SQLAlchemy database migrations for Flask applications using Alembic

Flask-Migrate Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. The database operations

Miguel Grinberg 2.2k Dec 28, 2022
A Python, Flask login system

Python Login System This is a basic login + authenticason system for flask using Flask_Login and Flask_SQLAlchemy Get started on your own To use this

MrShoe 0 Feb 02, 2022