403 CSRF verification failed on mobile android browser only

Hey everyone,

a django project I deployed in production gives me some headaches. I set up csrf tokens for a form post and it works if used from a desktop browser, but if the post is performed with a mobile android browser through a submit button, I get the following error:

Forbidden (403)
CSRF verification failed. Request aborted.

Help
Reason given for failure:

    Origin checking failed - https://<domain.com> does not match any trusted origins.
    
In general, this can occur when there is a genuine Cross Site Request Forgery, or when Django's CSRF mechanism has not been used correctly. For POST forms, you need to ensure:

Your browser is accepting cookies.
The view function passes a request to the template’s render method.
In the template, there is a {% csrf_token%} template tag inside each POST form that targets an internal URL.
If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.
The form has a valid CSRF token. After logging in in another browser tab or hitting the back button after a login, you may need to reload the page with the form, because the token is rotated after a login.
You're seeing the help section of this page because you have DEBUG = True in your Django settings file. Change that to False, and only the initial error message will be displayed.

You can customize this page using the CSRF_FAILURE_VIEW setting.

settings.py

from <project_name>.settings import *

DEBUG = True

SECRET_KEY = '<secret_key>'

ALLOWED_HOSTS = ['<domain.com>','www.<domain.com>']

CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

CSRF_TRUSTED_ORIGINS = [
    'https://www.<domain.com>',
    'http://www.<domain.com>'
]

INSTALLED_APPS = [
    '<app_name>',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]


LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'default': {
            'format': '[%(asctime)s] %(levelname)s: '
                      '%(message)s',
        }
    },
    'handlers': {
        'file': {
            'class': 'logging.handlers.'
                     'TimedRotatingFileHandler',
            'filename': '/var/log/<project_name>/'
                        '<project_name>.log',
            'when': 'midnight',
            'backupCount': 60,
            'formatter': 'default',
        },
    },
    'root': {
        'handlers': ['file'],
        'level': 'INFO',
    },
}

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.'
                   'FileBasedCache',
        'LOCATION': '/var/cache/<project_name>/cache',
    }
}

DATABASES = {
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.postgis',
        'NAME': '<project_name>',
        'USER': '<project_name>',
        'PASSWORD': '<project_name>',
        'HOST': 'localhost',
        'PORT': 5432,
    }
}

views.py

from django.http import HttpResponseRedirect
from django.shortcuts import render
from main_app.models.poll_model import Poll
from main_app.forms.poll_form import PollForm
from django.forms import modelformset_factory
from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie


# Create your views here.
def index(request):
    return render(request, '<app_name>/movies.html')


def movies_view(request):
    return render(request, '<app_name>/movies.html')


@csrf_protect
@ensure_csrf_cookie
def poll_view(request):
    """ Poll View """
    PollFormSet = modelformset_factory(Poll, form=PollForm, extra=0)
    if request.method == "POST":
        formset = PollFormSet(request.POST, request.FILES)
        if formset.is_valid():
            formset.save()
            return HttpResponseRedirect('')
    else:
        formset = PollFormSet()

    context = {'formset': formset}

    return render(request, '<app_name>/poll.html', context)

poll.html

{% extends "base.html" %}

{% block title %}Home{% endblock %}

{% block navbar %}
    <li class="nav-item">
        <a class="nav-link" href="/movies/">Movies</a>
    </li>
    <li class="nav-item">
        <a class="nav-link active" aria-current="page" href="/poll/">Poll</a>
    </li>
{% endblock %}

{% block content %}
<br/>
<form method="post">
{% csrf_token %}
<script>
    const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
</script>
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
    <table id="formset" class="form table table-dark table-striped">
    {% for form in formset.forms %}
        {% if forloop.first %}
            <thead>
            <tr>
                <th scope="col">User</th>
                <th scope="col">Mo</th>
                <th scope="col">Tu</th>
                <th scope="col">We</th>
                <th scope="col">Th</th>
                <th scope="col">Fr</th>
                <th scope="col">Sa</th>
                <th scope="col">Su</th>
            </tr>
            </thead>
        {% endif %}
        <tr class="{% cycle row1 row2 %}">
                <td> {{ form.instance }} </td>
                {% for field in form.visible_fields %}
                <td>
                    {# Include the hidden fields in the form #}
                    {% if forloop.first %}
                        {% for hidden in form.hidden_fields %}
                            {{ hidden }}
                        {% endfor %}
                    {% endif %}
                    {{ field.errors.as_ul }}
                    {{ field }}
                </td>
            {% endfor %}
        </tr>
    {% endfor %}
    </table>
    <input type="submit" value="Safe" class="btn btn-secondary">
</form>
{% endblock %}

The project runs on a Webserver with Nginx and Gunicorn with Ubuntu 20.04. For the encryption, “Let’s encrypt” is used.

Hey everyone, I fixed the error and here is the solution:

the nginx settings were missing:

location / {
    proxy_pass http://localhost:8000;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    client_max_body_size 20m;
}