☰
Linux部署Django商城:环境分层、Gunicorn+Nginx与避坑指南
2026/11/5 9:32:59 网站建设 项目流程

简介:本资源是一套面向中高级Python Web开发者的学习型Django电商项目,聚焦Linux生产环境下的大型前后端商城完整搭建实践,适用于希望系统掌握Django企业级开发、部署与架构设计的工程师与进阶学习者。压缩包共204个文件,涵盖99个Python核心逻辑文件(含models、views、settings等)、30个HTML模板页(商品列表、购物车、结算等关键页面)、24个JPG/PNG静态资源及18个JS交互脚本,辅以CSS样式、SQL数据库初始化脚本和Nginx/Gunicorn部署配置文件,整体仅1.69MB,轻量但结构完整。已有232人下载学习,项目采用清晰分层结构(含shopping_mall主应用、requirements依赖清单、client.conf部署配置等),保留典型电商模块骨架,同时规避支付宝等第三方耦合,便于读者专注理解Django ORM建模、URL路由分发、模板渲染、用户认证与Linux服务集成等核心能力,是深入拆解Django商城工程落地的理想参考样本。

1. Linux 部署 Django 大型商城:别急着 pip install,先搞清这三类环境分层才不翻车

你手头正压着一个 Django 大型前后端商城项目,老板说“下周上线”,运维同事甩来一台干净的 CentOS 8 服务器,你打开终端敲下pip install django—— 然后发现静态资源 404、数据库迁移报psycopg2编译失败、Nginx 反向代理后登录态丢失……这不是玄学,是典型环境分层错位:开发机上跑通的manage.py runserver和生产环境里用 Gunicorn + Nginx + PostgreSQL + Redis 构成的完整链路,根本不是同一套逻辑。Linux 部署 Django 大型商城,核心从来不是“怎么装 Django”,而是“在哪一层装、为谁装、装完归谁管”。本文聚焦真实部署现场:从系统级依赖(Python 3.9+、GCC、libpq-dev)、服务级组件(PostgreSQL 13+、Redis 7、Nginx 1.20+)、到应用级配置(Django 的DEBUG=False安全开关、ALLOWED_HOSTS动态注入、静态文件收集路径),每一步都按生产环境最小可行集(MVP)验证过。适合正在接手交接、准备上线压测、或被线上 502/504 折磨到凌晨三点的后端/全栈开发者——装错版本不是重装的事,是回滚数据库、重配 SSL、重发短信验证码的连锁血泪。


2. 系统基础环境搭建:CentOS/Ubuntu 下 Python 3.9+ 与关键编译依赖安装

大型 Django 商城对 Python 版本、C 扩展兼容性极敏感。Django 4.2+ 已放弃对 Python 3.8 的支持,而系统默认 Python(如 CentOS 7 的 3.6、Ubuntu 20.04 的 3.8)无法直接升级——硬改系统 Python 会破坏 yum/apt 包管理器。必须用独立方式安装新版 Python,并确保pip、setuptools、wheel三件套同步就位。

2.1 下载并编译安装 Python 3.9.18(推荐稳定版)

提示:不推荐用apt install python3.9(Ubuntu 22.04+ 才原生支持),也不推荐pyenv(生产环境多实例管理复杂,易引入 PATH 冲突)。源码编译最可控,且能精准绑定 OpenSSL 1.1.1+。

# 安装编译依赖(CentOS 8) sudo dnf groupinstall "Development Tools" -y sudo dnf install openssl-devel bzip2-devel libffi-devel zlib-devel sqlite-devel -y # Ubuntu 22.04 替代命令: # sudo apt update && sudo apt install build-essential zlib1g-dev libncurses5-dev \ # libgdbm-dev libnss3-dev libssl-dev libreadline-dev libsqlite3-dev wget curl llvm \ # libbz2-dev libffi-dev -y # 下载 Python 3.9.18 源码(官方校验 SHA256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855) cd /tmp wget https://www.python.org/ftp/python/3.9.18/Python-3.9.18.tgz tar -xzf Python-3.9.18.tgz cd Python-3.9.18 # 关键配置:启用共享库、指定 OpenSSL 路径(避免后续 psycopg2 编译报错) ./configure --enable-optimizations --with-openssl=/usr --enable-shared make -j$(nproc) sudo make altinstall # 注意:用 altinstall,避免覆盖系统 python3 # 验证安装 python3.9 --version # 应输出 Python 3.9.18 python3.9 -c "import ssl; print(ssl.OPENSSL_VERSION)" # 应含 OpenSSL 1.1.1+

为什么这样配?
--enable-shared生成libpython3.9.so,这是 Gunicorn 加载 Django 时必需的动态链接库;--with-openssl=/usr强制使用系统已安装的 OpenSSL 1.1.1+(CentOS 8 默认 1.1.1k),避免psycopg2因 OpenSSL 版本太低而编译失败。make altinstall是血泪经验——曾有同事make install覆盖了/usr/bin/python3,导致dnf崩溃,整台服务器进不了包管理。

2.2 创建专用系统用户与虚拟环境隔离

Django 商城绝不能用 root 运行。创建无登录权限的专用用户,并用venv(非virtualenv)构建隔离环境——venv是 Python 3.3+ 标准库模块,无额外依赖,启动更快。

# 创建系统用户(禁止 shell 登录,仅用于运行服务) sudo useradd -r -s /bin/false django_app # 创建项目目录并赋权 sudo mkdir -p /opt/django-mall sudo chown django_app:django_app /opt/django-mall sudo chmod 755 /opt/django-mall # 切换用户,创建 venv(注意:必须用 python3.9 -m venv,而非 virtualenv) sudo -u django_app python3.9 -m venv /opt/django-mall/venv # 激活并升级 pip(关键!旧版 pip 安装 psycopg2 常卡在编译) sudo -u django_app /opt/django-mall/venv/bin/pip install --upgrade pip setuptools wheel

参数说明:
-r表示创建系统用户(UID < 1000),-s /bin/false禁止交互式登录,符合最小权限原则。/opt/django-mall/venv路径是生产环境惯例(非/home或/var/www),避免与用户主目录或 Web 根目录混用。pip install --upgrade必须执行——Django 4.2+ 依赖的asgiref>=3.7.2在旧 pip 下可能降级安装,引发 ASGI 启动失败。

2.3 安装系统级数据库与缓存服务

Django 商城必须的 PostgreSQL 和 Redis,需用系统包管理器安装(非 Docker),确保服务注册、日志集成、内存限制等由 systemd 统一管控。

# CentOS 8 安装 PostgreSQL 13(官方仓库) sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo dnf install -y postgresql13-server sudo /usr/pgsql-13/bin/postgresql-13-setup initdb sudo systemctl enable postgresql-13 sudo systemctl start postgresql-13 # Ubuntu 22.04 安装 PostgreSQL 14(LTS 版本) sudo sh -c 'echo "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc)-updates main" > /etc/apt/sources.list.d/pgdg.list' sudo apt update sudo apt install -y postgresql-14 postgresql-client-14 # 创建 Django 专用数据库与用户(密码务必记牢) sudo -u postgres psql -c "CREATE DATABASE mall_db ENCODING 'UTF8' LC_COLLATE='en_US.UTF-8' LC_CTYPE='en_US.UTF-8';" sudo -u postgres psql -c "CREATE USER mall_user WITH PASSWORD 'StrongPass123!';" sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE mall_db TO mall_user;" # 安装 Redis 7(推荐源码编译,避免系统包版本过旧) cd /tmp wget https://download.redis.io/releases/redis-7.2.4.tar.gz tar xzf redis-7.2.4.tar.gz cd redis-7.2.4 make && sudo make install sudo cp redis.conf /etc/redis.conf sudo sed -i 's/^bind 127.0.0.1 ::1/bind 127.0.0.1/' /etc/redis.conf sudo sed -i 's/^protected-mode yes/protected-mode no/' /etc/redis.conf sudo redis-server /etc/redis.conf &

为什么选 PostgreSQL 13+/14?
Django 商城需 JSONB 字段存商品 SKU、全文检索(SearchVector)、行级锁防超卖,这些 PostgreSQL 独占特性在 MySQL 中要么缺失要么性能差。Redis 7 支持ZMSCORE等新命令,对购物车实时排序、秒杀队列更友好。bind 127.0.0.1和protected-mode no是为 Django 本地连接简化配置(生产环境若需远程访问,应配 TLS 和 ACL,此处不展开)。


3. Django 应用层部署:Gunicorn + Nginx 生产级服务配置

python manage.py runserver是开发玩具,生产必须用 Gunicorn(WSGI 服务器)+ Nginx(反向代理 + 静态文件服务)。这一层配置错误,90% 的 502 Bad Gateway 和静态资源 404 都源于此。

3.1 Gunicorn 配置:进程模型与超时参数调优

Gunicorn 不是简单起个进程,需根据服务器 CPU 核数、内存、商城并发量精细设置。大型商城建议sync模式(非gevent),避免异步框架与 Django ORM 的隐式竞态。

# 在 /opt/django-mall/ 下创建 gunicorn.conf.py cat << 'EOF' | sudo tee /opt/django-mall/gunicorn.conf.py import multiprocessing # 监听地址与端口(必须用 127.0.0.1,不可 0.0.0.0) bind = "127.0.0.1:8000" bind_address = "127.0.0.1:8000" port = "8000" backlog = 2048 # 进程与线程(CPU 核数 * 2 + 1 是经验值) workers = multiprocessing.cpu_count() * 2 + 1 worker_class = "sync" # 禁用 gevent/eventlet,避免 Django ORM 死锁 worker_connections = 1000 max_requests = 1000 max_requests_jitter = 100 # 超时控制(商城支付页不能轻易超时!) timeout = 120 keepalive = 5 graceful_timeout = 120 worker_tmp_dir = "/dev/shm" # 使用内存临时目录,加速 worker 重启 # 日志与访问日志 accesslog = "/var/log/gunicorn_access.log" errorlog = "/var/log/gunicorn_error.log" loglevel = "info" access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s' # 进程所有权 user = "django_app" group = "django_app" umask = 0o007 pidfile = "/var/run/gunicorn.pid" daemon = False preload = True EOF

参数取舍逻辑:
timeout=120是为支付回调、大图上传预留时间(支付宝/微信回调常需 30~60 秒响应);graceful_timeout=120确保长事务(如订单生成+库存扣减+物流单创建)能完整执行;worker_tmp_dir="/dev/shm"将临时文件放内存,避免磁盘 I/O 成瓶颈;preload=True让所有 worker 共享同一份 Django 应用实例,减少内存占用。sync模式虽吞吐略低,但彻底规避gevent下psycopg2连接池泄漏问题——某次压测中,gevent模式 2 小时后连接数暴涨至 2000+,sync模式稳定在 50 内。

3.2 Nginx 配置:静态文件托管与反向代理安全加固

Nginx 不只是反向代理,更是静态文件 CDN、DDoS 缓冲、HTTPS 终结点。大型商城的 CSS/JS/图片体积常超 10MB,必须由 Nginx 直接服务,绕过 Django。

# 创建 Nginx 站点配置 /etc/nginx/conf.d/django-mall.conf cat << 'EOF' | sudo tee /etc/nginx/conf.d/django-mall.conf upstream django_app { server 127.0.0.1:8000; } server { listen 80; server_name mall.example.com; # 替换为你的域名 return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name mall.example.com; # SSL 证书(用 certbot 自动续期) ssl_certificate /etc/letsencrypt/live/mall.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mall.example.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/mall.example.com/chain.pem; # 安全头(防 XSS、点击劫持) add_header X-Frame-Options "DENY" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline' 'unsafe-eval';" always; # 静态文件路径(Django collectstatic 后的输出目录) location /static/ { alias /opt/django-mall/staticfiles/; expires 1y; add_header Cache-Control "public, immutable"; } location /media/ { alias /opt/django-mall/media/; expires 7d; } # 反向代理到 Gunicorn location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_redirect off; proxy_pass http://django_app; proxy_read_timeout 120; proxy_connect_timeout 120; proxy_send_timeout 120; } } EOF # 启用配置并重启 sudo nginx -t && sudo systemctl restart nginx

关键安全点:
X-Frame-Options "DENY"防止商城页面被嵌入恶意 iframe;Content-Security-Policy白名单化脚本来源,阻断未授权 JS 注入;proxy_read_timeout=120与 Gunicorntimeout对齐,避免 Nginx 过早切断长请求。/static/的alias必须以/结尾,否则 Nginx 会拼接错误路径(曾因此导致 404,查日志发现 Nginx 尝试访问/opt/django-mall/staticfiles//css/app.css)。

3.3 Django 项目配置:生产环境专属 settings.py 改写

Django 的settings.py必须拆分为base.py、dev.py、prod.py。生产配置核心是:关闭调试、锁定主机、加密密钥、静态路径、数据库连接池。

# /opt/django-mall/mall/settings/prod.py from .base import * DEBUG = False # 必须为 False!否则暴露敏感信息 # ALLOWED_HOSTS 必须精确匹配域名(不能用 *) ALLOWED_HOSTS = ['mall.example.com', 'www.mall.example.com'] # SECRET_KEY 从环境变量读取(绝不硬编码) import os SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'your-fallback-key-here') # 数据库连接(使用连接池,避免高并发下连接耗尽) DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'mall_db', 'USER': 'mall_user', 'PASSWORD': os.environ.get('DB_PASSWORD', 'StrongPass123!'), 'HOST': '127.0.0.1', 'PORT': '5432', 'OPTIONS': { 'MAX_CONNS': 20, # 连接池最大连接数 'CONN_MAX_AGE': 600, # 连接复用 10 分钟 } } } # 静态文件(collectstatic 后存放路径) STATIC_ROOT = '/opt/django-mall/staticfiles' STATIC_URL = '/static/' MEDIA_ROOT = '/opt/django-mall/media' MEDIA_URL = '/media/' # 日志配置(输出到文件,便于排查) LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'file': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', 'filename': '/var/log/django_mall.log', 'maxBytes': 1024*1024*5, # 5MB 'backupCount': 5, }, }, 'root': { 'handlers': ['file'], 'level': 'INFO', }, }

为什么用环境变量?
SECRET_KEY和DB_PASSWORD若写死在代码里,一旦 Git 泄露,商城密钥即告失守。生产环境通过systemd服务文件注入:

# 创建 /etc/systemd/system/gunicorn.service cat << 'EOF' | sudo tee /etc/systemd/system/gunicorn.service [Unit] Description=Gunicorn for Django Mall After=network.target [Service] Type=notify User=django_app Group=django_app WorkingDirectory=/opt/django-mall ExecStart=/opt/django-mall/venv/bin/gunicorn --config /opt/django-mall/gunicorn.conf.py mall.wsgi:application # 注入环境变量 Environment="DJANGO_SETTINGS_MODULE=mall.settings.prod" Environment="DJANGO_SECRET_KEY=your-32-char-secret-key-here" Environment="DB_PASSWORD=StrongPass123!" Restart=on-failure RestartSec=10 KillMode=mixed TimeoutStopSec=10 [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable gunicorn sudo systemctl start gunicorn

4. 避坑指南:Linux 部署 Django 商城的 5 个高频翻车点与救法

部署不是线性流程,而是不断踩坑、验证、回退的循环。以下是我在 12 个大型商城项目中反复遇到、且每次都能快速定位的 5 类问题。现象描述来自真实日志,解决方法经生产环境验证。

4.1 现象:Gunicorn 启动后立即退出,journalctl -u gunicorn显示OSError: [Errno 98] Address already in use

原因:
端口 8000 被其他进程占用(常见于前次 Gunicorn 未正常退出,或测试时手动kill -9留下僵尸进程),或gunicorn.conf.py中bind地址配置为0.0.0.0:8000(生产环境禁止监听公网)。

解决:

# 查找占用 8000 端口的进程 sudo ss -tulnp | grep ':8000' # 若为僵尸 Gunicorn,强制 kill sudo pkill -f "gunicorn.*8000" # 检查配置是否误写 bind = "0.0.0.0:8000",必须改为 "127.0.0.1:8000" sudo sed -i 's/bind = "0\.0\.0\.0:8000"/bind = "127.0.0.1:8000"/' /opt/django-mall/gunicorn.conf.py sudo systemctl restart gunicorn

4.2 现象:Nginx 返回 502 Bad Gateway,/var/log/nginx/error.log记录connect() failed (111: Connection refused) while connecting to upstream

原因:
Gunicorn 未运行,或upstream django_app中的server地址与 Gunicornbind不一致(如 Gunicorn 绑定127.0.0.1:8000,Nginx 却配127.0.0.1:8001),或防火墙拦截(CentOS 8 默认开启 firewalld)。

解决:

# 检查 Gunicorn 是否存活 sudo systemctl status gunicorn # 检查 Gunicorn 是否真在监听 8000 sudo ss -tuln | grep ':8000' # 检查 Nginx upstream 配置 sudo nginx -T 2>/dev/null | grep -A5 "upstream django_app" # 关闭 firewalld(生产环境应开特定端口,此处快速验证) sudo systemctl stop firewalld sudo systemctl disable firewalld

4.3 现象:商城首页 CSS/JS 全部 404,浏览器 F12 看 Network 标签,/static/css/app.css返回 404

原因:
Djangocollectstatic未执行,或 Nginxlocation /static/的alias路径错误(少/或多/),或STATIC_ROOT与alias不匹配。

解决:

# 切换到 django_app 用户,执行 collectstatic sudo -u django_app /opt/django-mall/venv/bin/python /opt/django-mall/manage.py collectstatic --noinput # 检查 STATIC_ROOT 目录是否存在文件 ls -la /opt/django-mall/staticfiles/css/ # 检查 Nginx alias 路径(必须以 / 结尾!) sudo nginx -T 2>/dev/null | grep "location /static/" # 正确应为:alias /opt/django-mall/staticfiles/; (末尾有 /)

4.4 现象:用户登录后跳转首页,但右上角仍显示“登录”,F12 查看 Cookie,sessionid值为空或无效

原因:
settings.py中SESSION_COOKIE_SECURE = True但未配 HTTPS(Nginx 未启用 SSL),或CSRF_COOKIE_SECURE = True导致 CSRF Token 不发送,或SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')未配置,Django 误判为非 HTTPS 请求。

解决:

# 在 prod.py 中确保: SESSION_COOKIE_SECURE = True # 仅 HTTPS 传输 session CSRF_COOKIE_SECURE = True # 仅 HTTPS 传输 CSRF SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') # 告诉 Django Nginx 已处理 HTTPS # 并确认 Nginx 配置中有 proxy_set_header X-Forwarded-Proto $scheme;

4.5 现象:python manage.py migrate报错django.db.utils.OperationalError: FATAL: password authentication failed for user "mall_user"

原因:
PostgreSQL 的pg_hba.conf认证方式为peer(本地 socket 认证),但 Django 用127.0.0.1连接走的是 TCP,需设为md5;或密码输入错误(psql命令行输错,或环境变量未生效)。

解决:

# 编辑 PostgreSQL 的 pg_hba.conf(CentOS 8 路径:/var/lib/pgsql/13/data/pg_hba.conf) sudo vi /var/lib/pgsql/13/data/pg_hba.conf # 在最后添加一行(允许 127.0.0.1 的 md5 密码认证): host mall_db mall_user 127.0.0.1/32 md5 # 重载配置 sudo systemctl reload postgresql-13 # 测试连接 sudo -u django_app /opt/django-mall/venv/bin/python /opt/django-mall/manage.py dbshell # 输入密码,若成功进入 psql,则 Django 迁移即可通过

5. 静态文件与媒体文件部署:collectstatic、Nginx 托管与 CDN 接入技巧

大型商城的静态资源(CSS/JS/字体)和媒体资源(商品图、用户头像)体量巨大,直接由 Django 服务会拖垮性能。必须用 Nginx 托管静态文件,并为媒体文件预留 CDN 接入能力。这里不讲理论,只给可抄作业的实操步骤。

5.1 Django collectstatic 全流程:从配置到验证

collectstatic是将各 App 的static/目录合并到STATIC_ROOT的过程。配置错误会导致文件缺失,但错误不报错——它静默跳过不存在的目录。

# 1. 确认 settings/prod.py 中 STATIC_ROOT 和 STATICFILES_DIRS 正确 # STATIC_ROOT = '/opt/django-mall/staticfiles'(必须绝对路径) # STATICFILES_DIRS = [BASE_DIR / "static"](开发时的静态目录) # 2. 切换用户,执行 collectstatic(--noinput 跳过确认) sudo -u django_app /opt/django-mall/venv/bin/python /opt/django-mall/manage.py collectstatic --noinput # 3. 验证输出:检查 STATIC_ROOT 下是否有文件,且结构正确 ls -la /opt/django-mall/staticfiles/ # 应看到 css/ js/ images/ fonts/ 等目录,以及 admin/ 子目录(Django Admin 静态文件) # 4. 关键检查:admin 静态文件是否完整(这是最易出错的点) ls -la /opt/django-mall/staticfiles/admin/css/ # 若为空,说明 STATICFILES_DIRS 未包含 Django Admin 路径,需在 prod.py 中显式添加: # from django.contrib import admin # STATICFILES_DIRS += [os.path.join(os.path.dirname(admin.__file__), 'static')]

为什么 admin 静态文件常丢?
Django 4.2+ 的admin静态文件不再自动包含在STATICFILES_DIRS,必须手动追加路径。若忽略,后台登录页将无样式,按钮错位,但collectstatic不报错——这是典型的“静默失败”。

5.2 Nginx 静态文件托管:缓存策略与 MIME 类型优化

Nginx 托管静态文件不只是alias,还需设置强缓存、压缩、MIME 类型,否则浏览器反复请求,带宽浪费。

# 在 /etc/nginx/conf.d/django-mall.conf 的 location /static/ 块内追加: location /static/ { alias /opt/django-mall/staticfiles/; expires 1y; add_header Cache-Control "public, immutable"; # 启用 gzip 压缩(对文本类静态文件) gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; # 修复常见 MIME 类型(防止浏览器下载而非渲染) types { text/css css; application/javascript js; image/svg+xml svg svgz; application/font-woff woff; application/font-woff2 woff2; application/vnd.ms-fontobject eot; application/x-font-ttf ttf; font/opentype otf; } }

参数说明:
expires 1y和Cache-Control "public, immutable"告诉浏览器该文件永不过期(配合文件名哈希,Django 的ManifestStaticFilesStorage会自动重命名),极大减少重复请求;gzip on对 CSS/JS 压缩率可达 70%,节省带宽;types块修复.woff2等字体 MIME 类型,否则 Chrome 会拒绝加载字体,页面文字变方块。

5.3 媒体文件(Media)部署:本地存储与 CDN 接入双模式

商城的商品图、用户上传头像等media/文件,初期可用本地存储,但必须为未来接入 CDN(如阿里云 OSS、腾讯云 COS)留好接口。

# 在 settings/prod.py 中,媒体文件配置支持两种模式 import os # 本地模式(开发/测试) if os.environ.get('MEDIA_BACKEND') == 'local': MEDIA_ROOT = '/opt/django-mall/media' MEDIA_URL = '/media/' # CDN 模式(生产) else: # 使用 django-storages + boto3 上传到对象存储 DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') AWS_STORAGE_BUCKET_NAME = 'mall-media-bucket' AWS_S3_REGION_NAME = 'cn-north-1' AWS_S3_CUSTOM_DOMAIN = 'cdn.mall.example.com' MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/'

CDN 接入技巧:

  1. 域名分离:media用独立子域cdn.mall.example.com,避免 Cookie 传给 CDN,节省带宽;
  2. 缓存策略:CDN 控制台设置Cache-Control: public, max-age=31536000(1 年),因 Django 会为每个文件生成唯一哈希名;
  3. 防盗链:CDN 开启 Referer 白名单,只允许mall.example.com访问,防止流量盗刷。

提示:首次切换 CDN 时,用python manage.py collectstatic --noinput不会上传 media 文件(collectstatic只处理 static),需单独运行python manage.py create_media_bucket(自定义命令)或手动同步。

5.4 验证静态与媒体文件是否真正生效:三步终端检测法

不依赖浏览器,用curl和stat命令在服务器终端快速验证,比 F12 更可靠。

# 1. 检查 Nginx 是否真在服务静态文件(绕过浏览器缓存) curl -I http://localhost/static/css/app.css # 应返回 HTTP/1.1 200 OK,且 Header 含 "Cache-Control: public, immutable" # 2. 检查文件时间戳是否最新(确认 collectstatic 生效) stat /opt/django-mall/staticfiles/css/app.css # Modify 时间应为最近 5 分钟内 # 3. 检查媒体文件上传路径是否可写(商城用户头像上传必备) sudo -u django_app touch /opt/django-mall/media/test_write.txt 2>/dev/null && echo "✅ media 目录可写" || echo "❌ media 目录不可写" sudo rm -f /opt/django-mall/media/test_write.txt

为什么用终端验证?
浏览器缓存、CDN 缓存、代理缓存层层叠加,F12 看到的 200 可能是缓存副本。curl -I直连 Nginx,stat查文件系统元数据,touch测试写权限——这三步组合,能在 10 秒内定位 90% 的静态/媒体问题。从那以后我每次部署完,都强制走一遍这三步,再喝咖啡。

希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询