Django中Access-Control-Allow-Origin缺失的CORS问题求助
问题现状
已完成django-cors-headers的安装与基础配置,多数CORS错误已消除,但部署在web.mydomain.com的Flutter Web应用在Firefox中请求媒体资源时仍报错:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://example.com/media/images/image0_WEEP09I.png. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200
当前配置信息
settings.py关键配置
INSTALLED_APPS = [ 'rest_framework', # dasti ezafe shod 'rest_framework_simplejwt', 'corsheaders', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'carat', # dasti ezafe shod 'data', 'price', 'users', 'marketplace' ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', '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', ] CORS_ORIGIN_WHITELIST = ( 'https://example.com', 'https://web.example.com', )
自定义OPTIONS请求处理代码
def options(self, request): response = Response() response["Access-Control-Allow-Origin"] = "https://example.com" response["Access-Control-Allow-Methods"] = "GET, OPTIONS" response["Access-Control-Allow-Headers"] = "authorization" return response
接口正常响应头示例
HTTP/2 200 OK Content-Type: application/json Vary: Accept, origin,Accept-Encoding Allow: GET, HEAD, OPTIONS X-Frame-Options: DENY X-Content-Type-Options: nosniff Referrer-Policy: same-origin Cross-Origin-Opener-Policy: same-origin Access-Control-Allow-Origin: https://web.example.com Access-Control-Allow-Credentials: true Content-Length: 1884 Date: Sat, 03 Feb 2024 22:01:07 GMT Alt-Svc: h3=":443"; ma=2592000, h3-29=":443"; ma=2592000, h3-Q050=":443"; ma=2592000, h3-Q046=":443"; ma=2592000, h3-Q043=":443"; ma=2592000, quic=":443"; ma=2592000; v="43,46
问题根源
django-cors-headers仅处理经过Django视图的请求,而生产环境中媒体文件通常由Nginx/Apache等Web服务器直接返回,不会经过Django中间件,因此CORS响应头不会被自动添加到媒体资源的返回结果中。
可行解决方案
方案1:Web服务器层面添加CORS头(推荐生产环境)
以Nginx为例,在媒体资源的location配置块中添加:
location /media/ { # 原有媒体资源路径配置 alias /your/project/media/path/; # 强制添加CORS响应头 add_header Access-Control-Allow-Origin "https://web.mydomain.com" always; add_header Access-Control-Allow-Methods "GET, OPTIONS" always; add_header Access-Control-Allow-Credentials "true" always; # 直接处理OPTIONS预检请求 if ($request_method = OPTIONS) { return 204; } }
注:
always参数确保即使返回404/500等非成功状态码时,CORS头仍会被添加;若需允许多个源,可结合Nginx变量实现动态匹配。
方案2:让Django接管媒体资源请求(仅适合开发/小流量场景)
在项目根urls.py中添加媒体资源路由:
from django.conf import settings from django.conf.urls.static import static urlpatterns = [ # 原有业务路由配置 ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
此方法会让媒体请求经过Django中间件,从而触发
django-cors-headers的CORS头添加逻辑,但大流量场景下会增加Django服务压力,不推荐生产环境使用。
方案3:调整django-cors-headers配置(针对Django已处理媒体请求的场景)
如果媒体请求确实经过Django处理,可补充以下配置确保CORS规则覆盖媒体路径:
# settings.py CORS_ALLOWED_ORIGINS = [ "https://web.mydomain.com", "https://example.com", ] # 让CORS中间件处理媒体路径的请求 CORS_URLS_REGEX = r'^/media/.*$|^/api/.*$'
验证方式
修改配置后,在Firefox中重新请求媒体资源,通过开发者工具查看响应头是否包含Access-Control-Allow-Origin: https://web.mydomain.com,确认CORS错误是否消失。
内容的提问来源于stack exchange,提问作者Sina S

