#13217 closed Uncategorized (fixed)
LocaleMiddleware breaks caching. (Vary: Cookie)
Reported by: | harm | Owned by: | nobody |
---|---|---|---|
Component: | Uncategorized | Version: | 1.2-beta |
Severity: | Normal | Keywords: | session accessed vary cookie |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Enabling django.middleware.locale.LocaleMiddleware
causes that django adds a 'Vary: Cookie' header to every reponse.
The net effects of responses with Vary: Cookie, is that they are not properly cached by reverse proxies, and worse, never cached by end browsers. (Browser may cache requests with the same cookie, but in reality NO browser is implemented like that,
And this happens for _every_ response
Is there a way to achieve this without always touching the session ? So that we just get a Vary : Accept-Language
similar tickets with other middleware #6552
Change History (17)
comment:1 by , 15 years ago
comment:2 by , 15 years ago
Resolution: | → worksforme |
---|---|
Status: | new → closed |
I can't reproduce this. According to my testing, the locale middleware adds the "Vary: Accept-Language" and "Content-Language" headers. From a visual inspection of the code, I can't see anything that would modify the Cookie header.
If you insist this is happening, you'll need to provide a specific example of a view and configuaration that doesn't set Vary: Cookie until you turn on the Locale middleware.
comment:3 by , 15 years ago
Version: | 1.1-beta-1 → 1.2-beta |
---|
I selected the incorrect version -> 1.2-beta
The problem is that the LocaleMiddleware reads the request.session. The Session Middleware later detects that the session was touched (session.accessed) and appends the Vary: Cookie header.
class LocaleMiddleware(object): def process_request(self, request): language = translation.get_language_from_request(request) <----- accesses session.
comment:4 by , 15 years ago
here is a reproducible testcase.
Start with a new project
all default, no custom context processors, no custom middleware
views.y
from django.http import HttpResponse def hi(request): return HttpResponse("hi")
tests.py
from django.test import TestCase from views import hi class SimpleTest(TestCase): def test_hi(self): response = self.client.get('/hi') vary = response.get('vary',None) if vary: print "Vary: %s" % (vary) else: print " - no vary headers "
without django.middleware.locale.LocaleMiddleware
issue python manage.py test
. The result is
- no vary headers
add 'django.middleware.locale.LocaleMiddleware'
to the MIDDLEWARE_CLASSES
in settings.py
and issue python manage.py test
again. Now you get the incorrect result. A Cookie is added
Vary: Accept-Language, Cookie
comment:5 by , 15 years ago
Resolution: | worksforme |
---|---|
Status: | closed → reopened |
I made a copy/past error in the testcase tests.py
above. (missing vary = response.get('vary',None)
)
def test_hi(self): response = self.client.get('/hi') vary = response.get('vary',None) if vary: print "Vary: %s" % (vary) else: print " - no vary headers "
comment:6 by , 15 years ago
I don't have time to set up an environment to tests this right now, but just wanted to point that 1.2-beta 1 got bundled on Feb 06 and the [12546] and [12624] changes were committed after that (Feb 23 and March 01 respectively). If you are effectively using the beta1 tarball, can you retry with a newer SVN checkout?
comment:7 by , 15 years ago
Ok - I can now reproduce. The problem was that I had USE_I18N=False in my settings, which meant that the dummy translation functions were being used.
I can verify this also occurs on 1.1, so it isn't a recent change.
follow-up: 9 comment:8 by , 15 years ago
I can't test trunk right now.
But reading [12546] and [12624] i don't think they are related. They touch django caching stuff. Not the logic when to add Vary: headers
Thinking about this problem more, its actually quite hard to get this right. Where else as from the session can LocaleMiddleware his info ? With the current code ,if it reads the session, than automatically session.accessed gets set. If session.accessed is set -> Vary: Cookie is added.
Should we allow LocalMiddleware to read out a session via a backdoor? (because the LocalMiddleware _knows_ that it only looks at the language, and that it adds the correct Vary: Content-Language anyway. So that as fas as LocalMiddleware knows, Vary: cookie should not be set.
comment:9 by , 15 years ago
Resolution: | → wontfix |
---|---|
Status: | reopened → closed |
Replying to harm:
Should we allow LocalMiddleware to read out a session via a backdoor? (because the LocalMiddleware _knows_ that it only looks at the language, and that it adds the correct Vary: Content-Language anyway. So that as fas as LocalMiddleware knows, Vary: cookie should not be set.
Incorrect. It would be true if Django were *only* using the Accept-Language header to choose the page language. However, the session *is* being read to determine the site language, and a user can modify the site language using the i18n views. As a result, this means that Vary: Cookie is actually correct. The page *is* varying based on what is in the cookie, and as a result, it isn't cacheable independent of the cookie.
I completely agree that this is annoying from a page caching perspective, but I'm not sure I see what we can do about it, other than changing the way i18n works so that it doesn't have any dependency on the cookie -- but this would mean (1) breaking backwards compatibility, and/or (2) removing the ability for users to select their language.
Regrettably, I'm going to close this wontfix.
comment:10 by , 15 years ago
Yeah, I get (now) that this can't be solved for views that actually require translation.
Its rather unfortunate though , that using i18n renders the complete site 0% cachable. (except media/) Even parts of the site that are not related to i18n at all.
I really feel this is an undesired situation....
I still wonder if adding some settings/parameters. to LocaleMiddleWare can improve the current situation.
For example: the ability to exclude/include apps (by means of urls) from LocaleMiddleware. this prevents LocaleMiddleware from touching the session for urls where you as site builder you don't want translations.
That would give the freedom to explicitly control (if desired) what parts of your site you want to i18n enable. (or the reverse which parts of you site you want to exclude). All possible with backwards compatibility.
Any thoughts ?
comment:11 by , 15 years ago
In short, no, I don't have any ideas. If you want to follow this up, I suggest starting a thread in django-developers. It's a non-trivial topic, and it's going to require a lot of thought and discussion if we're going to get it right. Trac isn't the best place to have this discussion.
comment:12 by , 15 years ago
comment:13 by , 15 years ago
Keywords: | session accessed vary cookie added |
---|
follow-up: 17 comment:14 by , 12 years ago
Easy pickings: | unset |
---|---|
Resolution: | wontfix → fixed |
Severity: | → Normal |
Type: | → Uncategorized |
UI/UX: | unset |
If you're using i18n_patterns
(introduced in Django 1.4) and the URL contains a language prefix, get_language_from_request
will select the active language without reading the session.
So, in practice, this is fixed.
comment:15 by , 9 years ago
I don't think this is fixed.
For instance when using a CMS like FeinCMS you have to use LocalMiddleware but won't use i18n_patterns.
There are likely more use cases where localisation is required but i18n_patterns won't be used.
comment:16 by , 9 years ago
Do you have any ideas to address the issue? I haven't taken time to understand it but if you have a proposal, it's probably better to start a thread on the DevelopersMailingList as discussed above rather than comment on this old ticket which has been closed several times. Thanks.
comment:17 by , 3 years ago
Replying to Aymeric Augustin:
If you're using
i18n_patterns
(introduced in Django 1.4) and the URL contains a language prefix,get_language_from_request
will select the active language without reading the session.
So, in practice, this is fixed.
@Aymeric Augustin -- I believe there's an unhandled edge case if i18n_patterns receives prefix_default_language=False
: accept-language will still be set, even though the language of the page in question is static. I can't investigate more right now, and I know that
prefix_default_language
is fairly new, but leaving this here for posterity.
harm,
Are you really reporting this against 1.1-beta1?. If the answer is no, we need the exact release/beta/SVN revision at which you are seeing this.
#6552 was about a context processor not a middleware. Are you sure you are not using the same custom context processor that led you to erroneously reopen that ticket?