Linux部署Django商城:环境分层、Gunicorn+Nginx与避坑指南 简介本资源是一套面向中高级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 的DEBUGFalse安全开关、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.9Ubuntu 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 源码官方校验 SHA256e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 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.1CentOS 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 依赖的asgiref3.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 14LTS 版本 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_COLLATEen_US.UTF-8 LC_CTYPEen_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/14Django 商城需 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是开发玩具生产必须用 GunicornWSGI 服务器 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参数取舍逻辑timeout120是为支付回调、大图上传预留时间支付宝/微信回调常需 30~60 秒响应graceful_timeout120确保长事务如订单生成库存扣减物流单创建能完整执行worker_tmp_dir/dev/shm将临时文件放内存避免磁盘 I/O 成瓶颈preloadTrue让所有 worker 共享同一份 Django 应用实例减少内存占用。sync模式虽吞吐略低但彻底规避gevent下psycopg2连接池泄漏问题——某次压测中gevent模式 2 小时后连接数暴涨至 2000sync模式稳定在 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; modeblock 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防止商城页面被嵌入恶意 iframeContent-Security-Policy白名单化脚本来源阻断未授权 JS 注入proxy_read_timeout120与 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] DescriptionGunicorn for Django Mall Afternetwork.target [Service] Typenotify Userdjango_app Groupdjango_app WorkingDirectory/opt/django-mall ExecStart/opt/django-mall/venv/bin/gunicorn --config /opt/django-mall/gunicorn.conf.py mall.wsgi:application # 注入环境变量 EnvironmentDJANGO_SETTINGS_MODULEmall.settings.prod EnvironmentDJANGO_SECRET_KEYyour-32-char-secret-key-here EnvironmentDB_PASSWORDStrongPass123! Restarton-failure RestartSec10 KillModemixed TimeoutStopSec10 [Install] WantedBymulti-user.target EOF sudo systemctl daemon-reload sudo systemctl enable gunicorn sudo systemctl start gunicorn4. 避坑指南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 gunicorn4.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:8000Nginx 却配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 firewalld4.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 查看 Cookiesessionid值为空或无效原因settings.py中SESSION_COOKIE_SECURE True但未配 HTTPSNginx 未启用 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.confCentOS 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/xmlrss text/javascript; # 修复常见 MIME 类型防止浏览器下载而非渲染 types { text/css css; application/javascript js; image/svgxml 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 fhttps://{AWS_S3_CUSTOM_DOMAIN}/CDN 接入技巧域名分离media用独立子域cdn.mall.example.com避免 Cookie 传给 CDN节省带宽缓存策略CDN 控制台设置Cache-Control: public, max-age315360001 年因 Django 会为每个文件生成唯一哈希名防盗链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直连 Nginxstat查文件系统元数据touch测试写权限——这三步组合能在 10 秒内定位 90% 的静态/媒体问题。从那以后我每次部署完都强制走一遍这三步再喝咖啡。希望帮到你。本文还有配套的精品资源点击获取