Why doesn't Django's CSRF work over HTTPS?
Turns out it was an nginx configuration issue. My server setup is:
nginx -> nginx -> gunicorn
On the second nginx system, I had
proxy_set_header Host $host:$server_port;
However, since HTTPS is terminated at the first nginx, $server_port
was always 80.
On HTTPS, Django does strict referer checking (see point 4). Looking at the Django source:
good_referer = 'https://%s/' % request.get_host()
if not same_origin(referer, good_referer):
reason = REASON_BAD_REFERER % (referer, good_referer)
logger.warning('Forbidden (%s): %s', reason, request.path,
extra={
'status_code': 403,
'request': request,
}
)
return self._reject(request, reason)
CSRF validation was failing because "https://example.com/" != "https://example.com:80/"
.