feat: Add Retry-After HTTP header to lockout responses#1401
Open
rodrigobnogueira wants to merge 5 commits intojazzband:masterfrom
Open
feat: Add Retry-After HTTP header to lockout responses#1401rodrigobnogueira wants to merge 5 commits intojazzband:masterfrom
Retry-After HTTP header to lockout responses#1401rodrigobnogueira wants to merge 5 commits intojazzband:masterfrom
Conversation
added 2 commits
February 21, 2026 18:44
…OOLOFF_TIME` is configured, along with documentation and tests.
aleksihakli
requested changes
Mar 16, 2026
axes/helpers.py
Outdated
| return settings.AXES_PERMALOCK_MESSAGE | ||
|
|
||
|
|
||
| def _set_retry_after_header(response: HttpResponse, request: HttpRequest) -> None: |
Member
There was a problem hiding this comment.
Hey @rodrigobnogueira, would the Axes middleware be a good place for this?
Author
There was a problem hiding this comment.
Thanks @aleksihakli ,
moved Retry-After handling into AxesMiddleware so lockout response header logic is centralized there
axes/helpers.py
Outdated
| def _set_retry_after_header(response: HttpResponse, request: HttpRequest) -> None: | ||
| cool_off = get_cool_off(request) | ||
| if cool_off is not None: | ||
| response["Retry-After"] = str(int(cool_off.total_seconds())) |
Member
There was a problem hiding this comment.
Could settings this header be toggled with a flag?
Author
There was a problem hiding this comment.
Added AXES_ENABLE_RETRY_AFTER_HEADER (default False to not change current user's expectations) so projects can toggle this behavior.
…er-header # Conflicts: # axes/middleware.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
When
AXES_COOLOFF_TIMEis configured, lockout responses now automatically include theRetry-AfterHTTP header (RFC 7231 §7.1.3) with the cool-off duration in seconds. This helps clients (browsers, API consumers, bots)understand how long to wait before retrying.
Changes
axes/helpers.py_set_retry_after_header()helper that convertsAXES_COOLOFF_TIMEto total seconds and sets the
Retry-Afterheader on the response.get_lockout_response():JSON (XHR), template-rendered, and plain
HttpResponse.tests/test_helpers.pyRetry-Afterpresent with a valid cool-off time (plain response)Retry-Afterabsent whenAXES_COOLOFF_TIMEisNoneRetry-Afterpresent on JSON (XHR) responsesRetry-Afterpresent on template-rendered responsesRetry-Afterabsent on redirect responses (AXES_LOCKOUT_URL)docs/4_configuration.rst.. note::block after the settings table documenting theRetry-Afterheader behavior and which response types include it.Design Decisions
HttpResponseJsonResponse(XHR)AXES_LOCKOUT_URLAXES_LOCKOUT_CALLABLEAXES_COOLOFF_TIME=NoneTesting
All 351 existing + new tests pass with zero failures.
Before submitting