[Django] #29925: Redirect with HTTP/2 Server Push

29 views
Skip to first unread message

Django

unread,
Nov 6, 2018, 5:47:32 AM11/6/18
to django-...@googlegroups.com
#29925: Redirect with HTTP/2 Server Push
-------------------------------------+-------------------------------------
Reporter: Jaap Roes | Owner: nobody
Type: New | Status: new
feature |
Component: HTTP | Version: master
handling | Keywords: http2 server push
Severity: Normal | redirects
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
I was looking into upgrading nginx and reading it's changelog when I
noticed [https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/ HTTP/2
Server Push support was introduced semi-recently].

This reminded me of [https://twitter.com/simonw/status/1047865898717966337
this Tweet by Simon Willison] about accelerating redirects using a HTTP/2
Server Push header. I did some further research and stumbled upon
[https://www.ctrl.blog/entry/http2-push-redirects this article] about the
same concept.

This got me thinking about using this technique in Django.

There are a few places in Django's code base that will perform redirects
to "better" urls, e.g. when using `CommonMiddleware` and `APPEND_SLASH is
True` and/or `PREPEND_WWW is True` or using `LocaleMiddleware` i.c.w.
`i18n_patterns`. Wouldn't it be nice if these redirects also included a
`Link: <redirect-location>; rel=preload` header?

Would adding something like this be acceptable to the Django codebase? I'm
unsure if it's safe to include this header on non-http/2
connections/servers, but I'd assume it will be.

A step further would be to always do this for all
`HttpResponseRedirectBase` classes, or alternatively, introduce a
`Http2ServerPushRedirectBase` class.

--
Ticket URL: <https://code.djangoproject.com/ticket/29925>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Nov 6, 2018, 2:34:09 PM11/6/18
to django-...@googlegroups.com
#29925: Redirect with HTTP/2 Server Push
-------------------------------------+-------------------------------------
Reporter: Jaap Roes | Owner: nobody
Type: New feature | Status: new
Component: HTTP handling | Version: master
Severity: Normal | Resolution:
Keywords: http2 server push | Triage Stage:
redirects | Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Claude Paroz):

Interesting. Did you experiment such a configuration in a real project
successfully? In that case, I don't currently see what would prevent
implementing this optimization in Django.

--
Ticket URL: <https://code.djangoproject.com/ticket/29925#comment:1>

Django

unread,
Nov 7, 2018, 9:36:03 AM11/7/18
to django-...@googlegroups.com
#29925: Redirect with HTTP/2 Server Push
-------------------------------------+-------------------------------------
Reporter: Jaap Roes | Owner: nobody
Type: New feature | Status: new
Component: HTTP handling | Version: master
Severity: Normal | Resolution:
Keywords: http2 server push | Triage Stage:
redirects | Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Jaap Roes):

No, I did not experiment with this at all and was just basing all of this
on the Tweet and article I mentioned in the ticket. Sadly none of our
production servers have a recent enough nginx version, so I cannot do any
"real world" tests.

However, I did create a small `docker-compose` project that sets up nginx
and a Django project with a custom redirect class and a patched
`CommonMiddleware`. It seems to work pretty nice. You can check it out
here: https://github.com/leukeleu/django-push-redirect

--
Ticket URL: <https://code.djangoproject.com/ticket/29925#comment:2>

Django

unread,
Nov 7, 2018, 12:18:41 PM11/7/18
to django-...@googlegroups.com
#29925: Redirect with HTTP/2 Server Push
-------------------------------------+-------------------------------------
Reporter: Jaap Roes | Owner: nobody
Type: New feature | Status: new
Component: HTTP handling | Version: master
Severity: Normal | Resolution:
Keywords: http2 server push | Triage Stage: Accepted
redirects |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Claude Paroz):

* stage: Unreviewed => Accepted


Comment:

Nice! I guess the next step is to prepare a patch for Django.

--
Ticket URL: <https://code.djangoproject.com/ticket/29925#comment:3>

Django

unread,
Nov 8, 2018, 5:22:42 AM11/8/18
to django-...@googlegroups.com
#29925: Redirect with HTTP/2 Server Push
-------------------------------------+-------------------------------------
Reporter: Jaap Roes | Owner: nobody
Type: New feature | Status: new
Component: HTTP handling | Version: master
Severity: Normal | Resolution:
Keywords: http2 server push | Triage Stage: Accepted
redirects |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Jaap Roes):

After contemplating for a while on how to implement this nicely, I came to
the conclusion that the easiest/neatest way to do this would be to just
introduce a new middleware:

{{{#!python
from django.utils.http import is_safe_url

class Http2ServerPushRedirectMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
response = self.get_response(request)
if request.is_secure and response.status_code in {301, 302} and
hasattr(response, 'url'):
url = response.url
if is_safe_url(url, allowed_hosts={request.get_host()},
require_https=True):
response['Link'] = f'<{url}>; rel=preload'
return response
}}}

This works as long as this middleware is placed before `CommonMiddleware`.

I've updated the [https://github.com/leukeleu/django-push-redirect test
project] to use this approach as well.

Are you still interested in having a middleware like this in Django core?
Releasing this as a 3rd party package could be a sufficient solution as
well.

--
Ticket URL: <https://code.djangoproject.com/ticket/29925#comment:4>

Django

unread,
Nov 8, 2018, 2:15:58 PM11/8/18
to django-...@googlegroups.com
#29925: Redirect with HTTP/2 Server Push
-------------------------------------+-------------------------------------
Reporter: Jaap Roes | Owner: nobody
Type: New feature | Status: closed

Component: HTTP handling | Version: master
Severity: Normal | Resolution: wontfix

Keywords: http2 server push | Triage Stage: Accepted
redirects |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Claude Paroz):

* status: new => closed
* resolution: => wontfix


Comment:

Yes, in that case I guess having a 3rd-party app might be the way to go.
A request to integrate it into Django may come later when the app is well-
tested and HTTP/2 usage is a bit higher.

--
Ticket URL: <https://code.djangoproject.com/ticket/29925#comment:5>

Django

unread,
Dec 3, 2019, 10:09:36 AM12/3/19
to django-...@googlegroups.com
#29925: Redirect with HTTP/2 Server Push
-------------------------------------+-------------------------------------
Reporter: Jaap Roes | Owner: nobody
Type: New feature | Status: closed
Component: HTTP handling | Version: master
Severity: Normal | Resolution: wontfix
Keywords: http2 server push | Triage Stage: Accepted
redirects |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Jaap Roes):

I finally got around to packaging this up and releasing it to PyPI
https://pypi.org/project/django-push-redirect/

--
Ticket URL: <https://code.djangoproject.com/ticket/29925#comment:6>

Reply all
Reply to author
Forward
0 new messages