One day, I was sitting on the couch, watching the baseball game, when out of nowhere, I got like alerts. Some hacker was attempting to break through my /basicauth route by submitted thousands of requests with common username and password combinations. Oh no. This is what sparked me to figure out how to limit requests to a route, perhaps only 1 per second from the same IP address. Enter Flask-Limiter!
We will need to update our Flask app have the flask-limiter package. For example, if running Flask in a UI such as VSCode, the pip install command can be used.
pip install flask-limiter
Or, if running Flask in a Docker container, you can create a Docker image from a Dockerfile that has the flask-limiter package and then use the docker build command to create the Docker image.
FROM tiangolo/uwsgi-nginx-flask:latest
RUN apt-get update -y
RUN pip install --upgrade pip
RUN pip install flask-limiter
Let's update main.py to include the Limiter stuff.
from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
app = Flask(__name__)
limiter = Limiter(
get_remote_address,
app=app,
default_limits=["200 per day", "50 per hour"],
storage_uri="memory://",
)
@app.route("/foo")
def foo():
return "Hello"
@app.route("/bar")
@limiter.limit("1 per minute")
def bar():
return "World"
if __name__ == "__main__":
app.run()
There is no limit on the /foo route, so the /foo route should always return Hello. However, there is a "1 per minute" limit on the /bar route, so if you hit the /bar route twice, the second request should return something like this.
Almost always, Flask apps are not a single main.py file, and instead are comprised of multiple different files. Let's say your Flask app has a structure like this.
├── main.py
├── my-project (directory)
│ ├── __init__.py
│ ├── views.py
main.py could have the following.
from my-project import app
app = app()
if __name__ == '__main__':
app.run()
And __init__.py could have this.
from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
get_remote_address,
default_limits=["1000000 per day"],
storage_uri="memory://",
)
def app():
app = Flask(__name__)
from .views import views
app.register_blueprint(views, url_prefix='/')
limiter.init_app(app)
return app
And views.py could have this.
from flask import Blueprint
from . import limiter
views = Blueprint('views', __name__)
@views.route("/foo")
def foo():
return "Hello"
@views.route("/bar")
@limiter.limit("1 per minute")
def bar():
return "World"
Did you find this article helpful?
If so, consider buying me a coffee over at