File: index.rst

package info (click to toggle)
flask-limiter 3.12-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,264 kB
  • sloc: python: 6,432; makefile: 165; sh: 67
file content (479 lines) | stat: -rw-r--r-- 13,240 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
.. _pymemcache: https://pypi.python.org/pypi/pymemcache
.. _redis: https://pypi.python.org/pypi/redis
.. _github issue #41: https://github.com/alisaifee/flask-limiter/issues/41
.. _flask apps and ip spoofing: http://esd.io/blog/flask-apps-heroku-real-ip-spoofing.html

.. image:: _static/logo.png
    :target: /
    :width: 600px
    :align: center
    :class: logo

=============
Flask-Limiter
=============

.. currentmodule:: flask_limiter

.. toctree::
   :maxdepth: 2
   :hidden:

   strategies
   configuration
   recipes
   cli
   api
   development
   changelog
   misc

**Flask-Limiter** adds rate limiting to :class:`~flask.Flask` applications.

By adding the extension to your flask application, you can configure various
rate limits at different levels (e.g. application wide, per :class:`~flask.Blueprint`,
routes, resource etc).

**Flask-Limiter** can be configured to persist the rate limit state to many
commonly used storage backends via the :doc:`limits:index` library.


Let's get started!

Installation
============

**Flask-Limiter** can be installed via :program:`pip`.

.. code:: console

  $ pip install Flask-Limiter

To include extra dependencies for a specific storage backend you can add the
specific backend name via the ``extras`` notation. For example:

.. tab:: Redis

   .. code:: console

      $ pip install Flask-Limiter[redis]

.. tab:: Memcached

   .. code:: console

      $ pip install Flask-Limiter[memcached]

.. tab:: MongoDB

   .. code:: console

      $ pip install Flask-Limiter[mongodb]

.. tab:: Valkey

   .. code:: console

      $ pip install Flask-Limiter[valkey]


Quick start
===========
A very basic setup can be achieved as follows:

.. literalinclude:: ../../examples/sample.py
   :language: py

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

* Use an in-memory storage provided by :class:`limits.storage.MemoryStorage`.

  .. note:: This is only meant for testing/development and should be replaced with
     an appropriate storage of your choice before moving to production.
* Rate limiting by the ``remote_address`` of the request
* A default rate limit of 200 per day, and 50 per hour applied to all routes.
* The ``slow`` route having an explicit rate limit decorator will bypass the default
  rate limit and only allow 1 request per 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.

  .. tip:: 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://tools.ietf.org/html/rfc6585#section-4>`_ http error will be raised.

The extension adds a ``limiter`` subcommand to the :doc:`Flask CLI <flask:cli>` which can be used to inspect
the effective configuration and applied rate limits (See :ref:`cli:Command Line Interface` for more details).

Given the quick start example above:


.. code-block:: shell

   $ flask limiter config

.. program-output:: FLASK_APP=../../examples/sample.py:app flask limiter config
   :shell:

.. code-block:: shell

   $ flask limiter limits

.. program-output:: FLASK_APP=../../examples/sample.py:app flask limiter limits
   :shell:

The Flask-Limiter extension
---------------------------
The extension can be initialized with the :class:`flask.Flask` application
in the usual ways.

Using the constructor

   .. code-block:: python

      from flask_limiter import Limiter
      from flask_limiter.util import get_remote_address
      ....

      limiter = Limiter(get_remote_address, app=app)

Deferred app initialization using :meth:`~flask_limiter.Limiter.init_app`

   .. code-block:: python

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

At this point it might be a good idea to look at the configuration options
available in the extension in the :ref:`configuration:using flask config` section and the
:class:`flask_limiter.Limiter` class documentation.

-----------------------------
Configuring a storage backend
-----------------------------

The extension can be configured to use any storage supported by :pypi:`limits`.
Here are a few common examples:

.. tab:: Memcached

   Any additional parameters provided in :paramref:`~Limiter.storage_options`
   will be passed to the constructor of the memcached client
   (either :class:`~pymemcache.client.base.PooledClient` or :class:`~pymemcache.client.hash.HashClient`).
   For more details see :class:`~limits.storage.MemcachedStorage`.

   .. code-block:: python

      from flask_limiter import Limiter
      from flask_limiter.util import get_remote_address
      ....

      limiter = Limiter(
          get_remote_address,
          app=app,
          storage_uri="memcached://localhost:11211",
          storage_options={}
      )

.. tab:: Redis

   Any additional parameters provided in :paramref:`~Limiter.storage_options`
   will be passed to :meth:`redis.Redis.from_url` as keyword arguments.
   For more details see :class:`~limits.storage.RedisStorage`

   .. code-block:: python

      from flask_limiter import Limiter
      from flask_limiter.util import get_remote_address
      ....

      limiter = Limiter(
          get_remote_address,
          app=app,
          storage_uri="redis://localhost:6379",
          storage_options={"socket_connect_timeout": 30},
          strategy="fixed-window", # or "moving-window" or "sliding-window-counter"
      )

.. tab:: Redis (reused connection pool)

   If you wish to reuse a :class:`redis.connection.ConnectionPool` instance
   you can pass that in :paramref:`~Limiter.storage_option`

   .. code-block:: python

      import redis
      from flask_limiter import Limiter
      from flask_limiter.util import get_remote_address
      ....

      pool = redis.connection.BlockingConnectionPool.from_url("redis://.....")
      limiter = Limiter(
          get_remote_address,
          app=app,
          storage_uri="redis://",
          storage_options={"connection_pool": pool},
          strategy="fixed-window", # or "moving-window" or "sliding-window-counter"
      )

.. tab:: Redis Cluster

   Any additional parameters provided in :paramref:`~Limiter.storage_options`
   will be passed to :class:`~redis.cluster.RedisCluster` as keyword arguments.
   For more details see :class:`~limits.storage.RedisClusterStorage`

   .. code-block:: python

      from flask_limiter import Limiter
      from flask_limiter.util import get_remote_address
      ....

      limiter = Limiter(
          get_remote_address,
          app=app,
          storage_uri="redis+cluster://localhost:7000,localhost:7001,localhost:7002",
          storage_options={"socket_connect_timeout": 30},
          strategy="fixed-window", # or "moving-window" or "sliding-window-counter"
      )

.. tab:: MongoDB

   .. code-block:: python

      from flask_limiter import Limiter
      from flask_limiter.util import get_remote_address
      ....

      limiter = Limiter(
          get_remote_address,
          app=app,
          storage_uri="mongodb://localhost:27017",
          strategy="fixed-window", # or "moving-window" or "sliding-window-counter"
      )

The :paramref:`~Limiter.storage_uri` and :paramref:`~Limiter.storage_options` parameters
can also be provided by :ref:`configuration:using flask config` variables. The different
configuration options for each storage can be found in the :doc:`storage backend documentation for limits <limits:storage>`
as that is delegated to the :pypi:`limits` library.

.. _ratelimit-domain:

Rate Limit Domain
-----------------
Each :class:`~flask_limiter.Limiter` instance must be initialized with a
:paramref:`~Limiter.key_func` that returns the bucket in which each request
is put into when evaluating whether it is within the rate limit or not.

For simple setups a utility function is provided:
:func:`~flask_limiter.util.get_remote_address` which uses the
:attr:`~flask.Request.remote_addr` from :class:`flask.Request`.

Please refer to :ref:`deploy-behind-proxy` for an example.


Decorators to declare rate limits
=================================
Decorators made available as instance methods of the :class:`~flask_limiter.Limiter`
instance to be used with the :class:`flask.Flask` application.

.. _ratelimit-decorator-limit:

Route specific limits
---------------------

.. automethod:: Limiter.limit
   :noindex:

There are a few ways of using the :meth:`~flask_limiter.Limiter.limit` decorator
depending on your preference and use-case.

----------------
Single decorator
----------------

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

.. code-block:: python

   @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 of both.

.. code-block:: python

    @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 :class:`~flask_limiter.Limiter` instance
was initialized with. You can implement your own function to retrieve the key to rate limit by
when decorating individual routes. Take a look at :ref:`keyfunc-customization` for some examples..

.. code-block:: python

   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
    :doc:`flask request context <flask:reqcontext>`.

----------------------------------
Dynamically loaded limit string(s)
----------------------------------

There may be situations where the rate limits need to be retrieved from
sources external to the code (database, remote api, etc...). This can be
achieved by providing a callable to the decorator.


.. code-block:: python

   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():
       ...

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


.. note:: The callable is called from within a
   :doc:`flask request context <flask:reqcontext>` during the
   `before_request` phase.


--------------------
Exemption conditions
--------------------

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

.. code-block:: python

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

.. _ratelimit-decorator-shared-limit:

Reusable limits
---------------

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

.. automethod:: Limiter.shared_limit
   :noindex:


------------------
Named shared limit
------------------

.. code-block:: python

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

  @app.route("..")
  @mysql_limit
  def r1():
      ...

  @app.route("..")
  @mysql_limit
  def r2():
      ...


--------------------
Dynamic shared limit
--------------------

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

.. code-block:: python

   def host_scope(endpoint_name):
       return request.host
   host_limit = limiter.shared_limit("100/hour", scope=host_scope)

   @app.route("..")
   @host_limit
   def r1():
       ...

   @app.route("..")
   @host_limit
   def r2():
       ...


.. _ratelimit-decorator-exempt:

Decorators for skipping rate limits
-----------------------------------

Registering exemptions from rate limits
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. automethod:: Limiter.exempt
   :noindex:

.. _ratelimit-decorator-request-filter:

Skipping a rate limit based on a request
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This decorator marks a function as a filter for requests that are going to be tested 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.

.. automethod:: Limiter.request_filter
   :noindex:

.. code-block:: python

   @limiter.request_filter
   def header_whitelist():
       return request.headers.get("X-Internal", "") == "true"

   @limiter.request_filter
   def ip_whitelist():
       return request.remote_addr == "127.0.0.1"

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


For more complex use cases, refer to the :ref:`recipes:recipes` section.