I am working on a Django backend hosted on Heroku and a Flutter frontend hosted on https://anirni.web.app. My API requests are getting blocked due to CORS policy, and I’m struggling to configure Django to handle this properly
**My current setup:
**
- Backend: Django hosted on Heroku.
- Frontend: Flutter app hosted on https://anirni.web.app and also tested locally (http://localhost:52360).
- CORS library: I’m using django-cors-headers.
**Relevant parts of my settings.py:
**
`
"""
Django settings for anirni_server project.
Generated by 'django-admin startproject' using Django 5.1.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""
import os
from pathlib import Path
import dj_database_url
from corsheaders.defaults import default_headers
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
EMAIL_HOST = 'mail.anirni-dz.com' # Remplacez par le serveur SMTP de Bluehost
EMAIL_PORT = 465 # Port SMTP (généralement 587 pour TLS)
EMAIL_USE_TLS = False
EMAIL_USE_SSL = True # Activer SSL
EMAIL_HOST_USER = '###' # Adresse email
EMAIL_HOST_PASSWORD = '###' # Mot de passe
DEFAULT_FROM_EMAIL = '###'
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
from corsheaders.defaults import default_methods
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '####'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes', # Assurez-vous qu'elle est avant les autres apps comme 'auth'
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
"django_filters",
'corsheaders',
'rest_framework_simplejwt.token_blacklist', # Pour la gestion des tokens JWT
"django_crontab",
'basic_rest',
]
CORS_ALLOW_METHODS = list(default_methods) + ["PATCH"]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication', # Optionnel pour l'authentification par session
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20 # Nombre de résultats par page
}
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
]
MIDDLEWARE.insert(0, 'basic_rest.middleware.LogHeadersMiddleware')
CORS_ALLOWED_ORIGINS = [
"https://anirni.web.app", # Domaine de votre application Flutter
"http://localhost:52360", # Pour le développement local
]
ALLOWED_HOSTS = ['anirni.web.app', 'anirnirest-f97872c47bed.herokuapp.com']
CORS_ALLOW_CREDENTIALS = True # Autoriser les cookies/credentials
CORS_ALLOW_HEADERS = list(default_headers) + [
"authorization",
"x-requested-with",
]
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
ROOT_URLCONF = 'anirni_server.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'anirni_server.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME',"###"),
'USER': os.getenv('DB_USER',"###"),
'PASSWORD': os.getenv('DB_PASSWORD',"####"),
'HOST': os.getenv('DB_HOST',"###"),
'PORT': os.getenv('DB_PORT',"5432"),
}
}
DATABASES = {
'default': dj_database_url.config(conn_max_age=600, ssl_require=True)
}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO', # Niveau minimal de log (DEBUG, INFO, WARNING, ERROR, CRITICAL)
},
'__main__': { # Logger pour le middleware personnalisé
'handlers': ['console'],
'level': 'INFO',
},
},
}
# Configurer la durée du token
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
'AUTH_HEADER_TYPES': ('Bearer',),
}
**What I have tried so far:
**
- Adding CORS_ALLOW_ALL_ORIGINS = True temporarily, but the issue persists.
- Verifying that HTTPS is enabled and Heroku is correctly redirecting HTTP to HTTPS.
- Testing with curl and Postman, where responses seem fine, but requests from Flutter are blocked due to CORS.
**Expected behavior:
**
The Flutter frontend should be able to send requests (including OPTIONS preflight requests) to the Django backend without being blocked.
**Error from Flutter and browser console:
**
Access to XMLHttpRequest at ‘https://anirnirest-f97872c47bed.herokuapp.com/get_user_info’ from origin ‘https://anirni.web.app’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
**Additional details:
**
- The API uses rest_framework with token-based authentication (JWT).
- I’ve confirmed that my API endpoints are working independently.
- APIs for token retrieval and public APIs work perfectly. However, all APIs that require authentication (using JWT) are being blocked.
2
Answers
Just run this command
instead of
also, you can change the port in the first command I just used the default one
To resolve the CORS issue, ensure that the CorsMiddleware is placed at the top of the MIDDLEWARE list, above all other middleware that processes the request. Additionally, make sure to configure the CORS_ALLOW_HEADERS to allow necessary headers, such as content-type
]
]