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/".