I have a Django view with a form that uses CSRF protection. I want this view to be cached by Varnish when there is a normal GET request (since all users need the same form, no login).
So there are two challenges:
How to cache this page in Varnish and not delivered cached/old versions of the csrf hidden field to the user? Is it at all possible to cache pages with CSRF field?
My Varnish by default strips out all cookies, how could I easily make it strip all cookies, except the csrftoken cookie? And do I have to set a specific CSRF_COOKIE_DOMAIN?
This is a couple of years late, but here's how I got around this problem recently.
The trick is to use ESI, which varnish supports. We take the CSRF snippet and stick it into its own page, including it via ESI when going through varnish, and directly otherwise (such as when running the local dev server).
{% csrf_token %}
{% if request.META.HTTP_X_VARNISH_USE_CACHE %}
<esi:include src="{% url 'esi_csrf_token' %}" />
{% else %}
{% include "csrf_esi.html" %}
{% endif %}
from django.conf.urls import url
from django.views.generic import TemplateView
urlpatterns = [
...
url(r'csrf_esi.html', TemplateView.as_view(template_name="csrf_esi.html"), name='esi_csrf_token'),
]
from django import template
register = template.Library()
@register.inclusion_tag('csrf_token.html', takes_context=True)
def csrf_token_esi(context):
return context
TEMPLATES = [
{
...
'OPTIONS': {
...
'builtins': [
'path.to.csrf_esi',
],
}
}
]
set req.http.X-Varnish-Use-Cache = true;
You also need to whitelist the csrf_esi.html
page so it never gets cached and add set beresp.do_esi = true;
inside the vcl_fetch
function. I'd elaborate more on this, but I didn't set this part of the system up and am not 100% clear myself.
Now you can simply use it like you do the normal {% csrf_token %}
tag:
<form action="">
{% csrf_token_esi %}
<button type="submit">Push me</button>
</form>
It's quite a bit to set up, but once you do, you'll never have to look at it again.
Using CSRF on a view essentially means that each render of the view is inherently different (even though only the value of one hidden field is changing). Caching doesn't work in such a scenario.
However, Django does provide mechanisms for getting around this limitation, namely cookies, as you seem to already have guessed. So on your second part, there's two things that need to be done:
You only need to set CSRF_COOKIE_DOMAIN
in Django if the request will be coming from a different domain than where it is processed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With