简介:围绕CentOS系统与宝塔面板部署Django应用的完整流程,整理为一份简明PDF教程,适合需要在Linux服务器上快速上线Django项目的开发人员、运维人员及刚接触服务器部署的初学者使用。教程从宝塔面板与Python项目管理器、Nginx的安装准备讲起,覆盖项目代码上传的Git克隆与FTP两种方式、创建Django项目时路径、Python版本、uWSGI启动方式及端口等关键配置,随后通过映射绑定域名或外网IP,并在Nginx反向代理中补充static与media静态资源路径,最后完成项目重启与Nginx配置重载。针对部署中常见的环境准备、端口选择、静态文件丢失等问题均有明确操作指引,步骤清晰、路径标注具体,可帮助读者从零开始将本地Django应用发布到公网。资源为单个PDF文件,大小139KB,便于本地查阅或打印对照操作。该教程已获得2371人学习下载,尤其适合用于个人博客、小型Web应用的生产环境部署参考。
1. CentOS下宝塔部署Django项目:从零到上线,一个下午能走完的路
CentOS下宝塔部署Django项目,本质上是在回答一个问题:没有专职运维的情况下,怎么用一台 Linux 服务器把 Django 业务跑稳。拿我自己的经历说,2核4G的 CentOS 7,从装系统到域名能访问,一个下午能走完,其中一半时间还花在等 pip 下载上。宝塔面板解决的是 Nginx、MySQL、日志这些系统级杂活,Django 代码仍然走标准 WSGI 协议,由 Gunicorn 承载,两者各管一段。适合正在做 django 项目实战、想快速验证自己作品成品的开发者,也适合给小团队内部系统当部署底座。这篇文章按真实部署顺序来:系统、面板、Python、数据库、进程、反代、排错,每一步都给出能直接抄的命令和参数解释。
2. 先搭地基:CentOS 7.9 镜像、yum 源与宝塔面板安装的三件事
2.1 镜像选型:为什么 CentOS 7.9 仍是部署默认项
选系统时,热词里的 centos 7.9下载 和 centos 8 stream下载 背后是同一类纠结。CentOS 8 在 2021 年底停止了维护,Stream 是滚动更新版,不适合当生产底座;CentOS 7.9 作为最后的经典版本,虽然官方源也进了 vault 归档,但宝塔对 CentOS 7 的兼容性和国内镜像的覆盖仍然最完整。新手或者团队服务器建议直接选 7.9 x86_64,如果拿到的机器是 ARM 架构,就要去镜像站找 arm版centos下载 对应的 aarch64 镜像,两者在宝塔安装上没有区别,但 gunicorn 里的二进制依赖少,ARM 版可能需要多编译一步。
CentOS 和 Ubuntu 的选择也是老话题。Ubuntu 的 apt 源软件包新,跑 Python 项目很顺,但很多从教程抄来的 systemd 和防火墙写法在 Ubuntu 上会翻车,因为它的网络管理默认是 netplan,而 CentOS 7 是纯 network.service 体系。如果你只为了部署一个 Django 应用,CentOS 7.9 的坑最少,网上能找到的案例也最多,这也是宝塔默认把这套组合当主场景的原因。安装时用最小化镜像即可,不要选带桌面环境的版本,服务器不跑图形界面。ISO 在阿里云镜像站或华为云镜像站都有历史目录,文件名类似 CentOS-7-x86_64-Minimal-2009.iso。装完虚拟机之后,如果发现主机内容不能复制到虚拟机里面,这不是系统问题,VMware 环境里要装 open-vm-tools,服务器上用 git 拉代码、用 scp 传压缩包都比剪贴板可靠。
2.2 换源:CentOS 7.9 的 yum 到底怎么救
CentOS 7.9 官方源已经停止更新,直接 yum install 多半连不上 mirrorlist。先备份原源文件,再切到国内镜像:
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo yum clean all yum makecache yum install -y wget vim net-toolscurl 下载的是阿里云的 CentOS-7.repo 模板,里面已经写好 baseurl 指向 mirrors.aliyun.com,不需要再改。makecache 速度如果能跑到几 MB/s,说明源没有问题。wget、vim、net-tools 这三件套是后面手动操作的基础,宝塔安装脚本本身也要用 wget。如果手上是 CentOS 8 或者更老的版本,换网易源或阿里源同样适用,核心是修改 baseurl 指向镜像站对应版本的目录。改完源后检查一下磁盘占用,df -h,系统分区如果只有 20G,后面 Python 虚拟环境和 npm 构建产物很容易把 / 打满,这就是热词里 centos扩容 的来源。扩容要在系统装完、数据迁入之前做,进 PE 或直接在面板的磁盘工具里扩根分区最稳。
2.3 宝塔面板安装:命令、登录和五条初始安全项
安装宝塔面板只有一条命令:
yum install -y wget && wget -O install.sh https://download.bt.cn/install/install_6.0.sh && sh install.sh安装过程大概两到三分钟,脚本会检测系统版本并装好 Nginx、MySQL 等组件的编译环境。结束时会打印面板访问地址、默认账号和密码,这个信息只在终端出现一次,务必复制到本地的密码管理工具里。如果中途断网,重新执行 install.sh 可以续装,宝塔的安装脚本是幂等的,反复执行不会装出两份面板。
登录面板后我一般先做四件事:第一,在面板设置里把默认 8888 端口改成不常见端口;第二,开启面板 SSL 并绑定一个域名;第三,在安全菜单里把 SSH 和面板的入口 IP 白名单配上;第四,到软件商店把 Nginx、MySQL、Python 项目管理器这三个插件装上。bt 命令是面板自带的命令行工具,输入 bt 会列出 15 个左右的功能编号,包括改端口、改密码、查看面板信息。这一步别跳过,公网 IP 的 8888 端口几乎每分钟都在被扫描,改端口是最基本的自我保护。宝塔面板搭建网站这个搜索词下很多人问网站和 Python 项目什么区别。简单说,网站菜单只处理静态站点和 PHP 项目,Django 应用要用软件商店里的 Python 项目管理器来管理进程,所以下面一章先解决 Python 环境和依赖的问题。
3. Django 项目上线前的三件套:Python 版本、虚拟环境与 SQLite/MySQL 选型
3.1 用面板的 Python 项目管理器装解释器,为什么不要动系统 Python
在开始部署 Django 之前,先把一个血泪经验说在前面:永远不要在 CentOS 7 上直接替换系统自带的 Python。CentOS 7 的 yum 核心工具链依赖 python2.7,把 /usr/bin/python 软链改成 python3,yum 立刻全部报错,连面板安装脚本都会跟着挂。宝塔的 Python 项目管理器会把解释器编译到 /www/server/panel/pyenv 目录,和系统 Python 完全隔离,这才是安全做法。
打开软件商店,安装 Python 项目管理器,版本选 3.9 或 3.10 都行。Django 3.2/4.x 对这两个版本支持很好,不需要追最新的 3.12。面板默认提供的版本列表来自官方 pyenv,编译一个解释器大概五分钟,这期间能做的就是把项目代码先传到服务器。代码放 /www/wwwroot 下,目录所有权改成 www 用户,因为后面的 Nginx 和面板进程都是以 www 身份运行的。项目进来之后,先用 SSH 进入项目目录创建虚拟环境:
cd /www/wwwroot/myproject python3 -m venv venv source venv/bin/activate pip install --upgrade pip pip install django gunicorn mysqlclient pip freeze > requirements.txtpython3 这里指向的是面板编译的解释器,如果你是在面板终端里操作,环境变量已经配好;如果用自己的 SSH 客户端,用 which python3 确认路径包含 pyenv。创建虚拟环境后,所有依赖都装在 venv 里,将来删项目直接整目录删掉,不会污染系统。pip install 后面跟的包循环安装失败时,最常见原因是网络,换成 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 再装。如果项目是 git 仓库,直接 git pull 到 /www/wwwroot/myproject 即可,不需要用宝塔的上传功能传整个包,那样容易漏掉 .env 这类隐藏文件。传完之后检查一下项目的 manage.py 在不在项目根目录,这个路径后面填面板表单要用。
3.2 创建 Django 项目:startproject 还是已有仓库,settings 怎么改
没有现成代码时,在虚拟环境里直接创建项目:
source venv/bin/activate django-admin startproject config . python manage.py migrate python manage.py createsuperuserstartproject 后面的点号表示把 config 配置目录建在当前目录,而不是再套一层子目录。migrate 先跑掉内置的 auth/session 等十几张表,createsuperuser 创建后台管理员账号。这一步完成之后,项目还不能对外提供服务,Django 默认监听 127.0.0.1:8000,只能本机访问。
上线关键的 settings.py 改三项。ALLOWED_HOSTS 要加服务器 IP 和域名,否则外网访问直接报 DisallowedHost 400;DEBUG 改成 False,开启后 Django 不再返回堆栈信息,避免暴露项目结构,同时必须在 settings.py 里配好静态文件收集路径:
ALLOWED_HOSTS = ['your_domain.com', '123.123.123.123'] DEBUG = False STATIC_ROOT = BASE_DIR / 'static' STATIC_URL = '/static/'STATIC_ROOT 让 Django 能把 admin 自带样式和业务静态文件统一收集到项目目录下的 static 文件夹,后续 Nginx 直接指向这个目录。如果项目里有上传图片功能,MEDIA_ROOT 也要单独指定一个目录,并确保 www 用户对该目录有写权限。这些参数改完再一次 python manage.py collectstatic --noinput 收集静态文件,输出里会显示 copied N 个文件,数量对得上 admin 资源就说明路径没配错。django 创建 app 是新手阶段最常做的事,部署时每个 app 的 models 改动要靠 python manage.py makemigrations 和 migrate 同步到数据库,上线前记得把所有未提交的迁移文件一起提交,服务器拉取后统一执行 migrate。
3.3 数据库选型:SQLite 零配置起步,MySQL 迁移的三个参数
sqlite怎么在宝塔面板安装、宝塔面板安装sqlite 这类搜索词背后其实是同一种误解:Django 的 SQLite 驱动不需要单独安装,只要 Python 解释器编译时带了 sqlite3 模块,虚拟环境里直接就能用。验证方式是在 Django shell 里执行:
import sqlite3 print(sqlite3.sqlite_version)能输出 3.x.x 就说明驱动可用。宝塔的 Python 项目管理器编译解释器时默认启用 sqlite3,所以 SQLite 路线是零配置的,数据库文件就是项目目录下的 db.sqlite3。SQLite 适合单机低并发项目,Django admin 后台、个人博客、内部工具,完全够用。
提示:如果 SQLite 频繁写入报 database is locked,先检查项目目录的所有者是不是 www,而不是急着换 MySQL。
项目原本用 MySQL,迁移时把 settings.py 的 DATABASES 改成引擎 mysql,还要确认驱动。面板软件商店装 MySQL 5.7,然后在虚拟环境里 pip install mysqlclient。mysqlclient 在 CentOS 上编译依赖 mysql-devel,如果编译失败,先 yum install -y mysql-devel gcc python3-devel。Django 连接 MySQL 时,数据库名、用户、密码三项要和面板里建的一致:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django_db', 'USER': 'django_user', 'PASSWORD': 'your_password', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': {'charset': 'utf8mb4'}, } }charset 用 utf8mb4 是为了存 emoji 和生僻字,MySQL 5.7 默认字符集在部分版本里是 utf8mb3,不显式指定容易乱码。数据库从 SQLite 迁到 MySQL 时,注意 django 执行查询-删除对象 的级联行为:SQLite 默认外键松散,MySQL 的 InnoDB 会校验外键,删除父表记录前要么显式指定 CASCADE,要么先清掉关联子表。ORM 代码里用 on_delete=models.CASCADE 的模型一般没问题,手动执行 delete 时就要小心。
4. 核心部署链路:Gunicorn 启动、Nginx 反代与静态文件收集一次配通
4.1 Python 项目管理器的启动表单:入口路径和 worker 参数怎么填
面板的 Python 项目管理器里添加项目,最关键的两个字段是项目路径和启动命令。项目路径选 /www/wwwroot/myproject,启动命令不要写 python manage.py runserver,那是开发模式,单进程扛不住访问,也不能常驻。生产环境用 Gunicorn:
gunicorn config.wsgi:application -b 127.0.0.1:8000 --workers 2 --threads 4 --timeout 30config.wsgi:application 是 WSGI 入口,config 对应项目里的 config 目录,wsgi 对应 wsgi.py,冒号后面是 application 对象。写错入口最常见的错误是直接用 manage.py,Gunicorn 不认识它。-b 127.0.0.1:8000 表示只监听本机 8000,端口要等 Nginx 反代,不要监听 0.0.0.0,否则等于是把 Django 裸露到公网。workers 参数是进程数,我的经验是 CPU 核心数乘以 2 加 1,2 核机器填 3,4 核填 5,不是越多越好,worker 多了内存直接翻倍。threads 参数用于并发,Django 的 ORM 查询是 IO 密集,填 4 到 8 够应付常规场景。项目里有耗时上报或没有同步长任务的,workers 和 threads 可以适当调小,2核机器填 2+4 很稳。
填完启动以后,面板这个项目卡片显示运行中,进程列表能看到 master 和 worker。想验证服务真的起了,SSH 里 curl 一下:
curl -I http://127.0.0.1:8000/返回 HTTP/1.1 200 就说明 WSGI 管线通了,后面只差 Nginx。如果面板项目启动失败,先别急着改面板配置,在项目目录手动执行同样的 gunicorn 命令,错误日志直接打在终端,比面板日志直观得多。面板的日志一般存在 /www/wwwlogs 或项目目录下的 .python-manager 目录里,排查时用 tail -f 跟着看。顺带提醒一句,项目里如果用了 python django websocket 做后台数据推送前端,Gunicorn 默认 worker 不支持 WebSocket 协议,要换 uvicorn 或 daphne 作为启动命令,Nginx 侧还要补一段 Upgrade 头配置,这是后话,但选型时得先知道。
4.2 Nginx 反代的最小配置:location 顺序、静态文件与请求头
Nginx 安装好后到网站菜单添加站点,域名填你的域名或服务器 IP,根目录随便填一个项目外的目录,接下来编辑配置文件,把 server 块改成下面这样:
server { listen 80; server_name your_domain.com; client_max_body_size 20m; location /static/ { alias /www/wwwroot/myproject/static/; expires 7d; access_log off; } location /media/ { alias /www/wwwroot/myproject/media/; } location / { proxy_pass http://127.0.0.1:8000; 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; } }location /static/ 和 /media/ 两个块放在 location / 前面,Nginx 匹配规则是前缀优先且最长匹配,/static/ 开头的请求不会落到反代。alias 是替换关键,/static/css/a.css 会映射到 /www/wwwroot/myproject/static/css/a.css,这个路径要和实际 collectstatic 的输出目录完全一致。如果 alias 写成 /www/wwwroot/myproject/static/static,访问 404,那就是路径多套了一层。
proxy_pass http://127.0.0.1:8000 不带 URI,Nginx 会把请求原样转发给 Gunicorn,Django 拿到的是完整路径。四个 proxy_set_header 很关键,Host 不传的话 Django 的 ALLOWED_HOSTS 校验会失败,X-Forwarded-For 用来记录真实客户端 IP,X-Forwarded-Proto 保证重定向时能拿到 https 前缀。这些头在 Django 侧通过 request.META 读取。保存配置后执行 nginx -t 检查语法,再 reload。不要直接重启 Nginx,reload 是平滑重载不中断已有连接。确认配置语法后,浏览器访问域名,能看到页面说明整条链路通了。此时 SSH 里执行 ss -lntp,能看到 Nginx 监听 :80,Gunicorn 监听 127.0.0.1:8000。很多框架的宝塔部署,比如 nuxt4部署宝塔详细流程,本质都是同一套 Nginx 反代手法,只是后端进程从 gunicorn 换成了 node 的 pm2。理解 location 和 proxy_pass 的规则,换个项目只是改入口进程的问题。
4.3 防火墙、SELinux 与端口监听:三个容易被忽略的环境变量
CentOS 7 默认 firewalld,检查一下端口开放情况:
firewall-cmd --state firewall-cmd --list-all如果只是通过域名访问,80 和 443 通常默认放行,不需要额外操作。如果想把 8000 暴露出来临时调试,必须执行:
firewall-cmd --zone=public --add-port=8000/tcp --permanent firewall-cmd --reload测试完记得删掉这条规则,Django 被公网直接访问没有任何安全套层,所有请求都会绕过 Nginx 的日志和连接限制。SELinux 是比防火墙更隐蔽的坑。默认 Enforcing 模式下,Nginx 作为非标准进程去连本机的 8000 端口会被 SELinux 拦截,表现是页面上 502,Nginx error.log 里报 connect() failed (13: Permission denied)。临时关掉试一下:
setenforce 0 getenforce如果确实是 SELinux 的问题,用持久化配置解决,不要每次重启都手动关。执行下面的命令,允许 Nginx 访问本机网络端口:
setsebool -P httpd_can_network_connect 1这个布尔值允许 httpd 相关的 Nginx 进程访问外网网络。用面板部署时,宝塔默认没有处理 SELinux,遇到 502 先看这一项。系统的 /etc/selinux/config 可以改成 disabled,但我不建议,保持 Enforcing 配合 setsebool 更安全,毕竟一台公网服务器上不止 Django 一个服务。
5. 避坑清单:宝塔上跑 Django 最常踩的 6 个翻车现场
5.1 面板项目启动失败:日志显示 ModuleNotFoundError
现象:在 Python 项目管理器点击启动,项目状态一直在启动中,两三分钟后变红。查看日志,常见 ModuleNotFoundError: No module named 'django'。
原因:面板启动项目时用了错误的环境。项目管理器默认关联刚创建的虚拟环境,但如果你在面板里手动改了 Python 版本,或项目是后面才放进来的,面板没有自动绑定 venv。
解决:项目路径填完后,版本选择要精确到当初建 venv 用的那个解释器,然后在启动命令前显式激活虚拟环境,手动启动验证:
source /www/wwwroot/myproject/venv/bin/activate pip list | grep django gunicorn config.wsgi:application -b 127.0.0.1:8000终端能起来,再回面板把启动方式改成命令模式。如果终端也起不来,pip list 里没有 django,就是依赖没装进去,直接 pip install -r requirements.txt 重新装。
5.2 改完代码刷新页面没变化:Gunicorn 不会自动重载
现象:本地 runserver 时代改个 views.py 保存就生效,部署到宝塔后改完代码,浏览器刷新还是旧页面。
原因:Gunicorn 是常驻进程,worker 加载代码后不会监听文件变化,runserver 的 auto reload 在 WSGI 生产模式下不存在。
解决:启动命令加 --reload 参数,Gunicorn 会像开发模式一样监测文件变更,但生产环境我不建议长开,多一次文件监听就多一分系统调用开销。正确做法是改完代码后,在面板项目页点重启,或者 SSH 里给 Gunicorn master 进程发 HUP 信号:
kill -HUP $(cat /www/wwwroot/myproject/gunicorn.pid)HUP 会让 worker 优雅重启,正在处理的请求不会中断,比粗暴 kill 再启动要稳得多。gunicorn.pid 文件需要在启动命令里加 --pid 参数才会生成,面板的命令模式默认不生成,手动执行时记得带上。
5.3 CSS/JS 全部 404:DEBUG=False 后静态文件没人管了
现象:页面结构正常,但所有样式和图片 404,admin 后台同样没有 CSS。
原因:DEBUG=True 时 Django 自己托管静态文件,部署时改成 False,Django 就不再处理 /static/ 请求,必须交给 Nginx 或 whitenoise。
解决:确认三步都做过:settings.py 里配了 STATIC_ROOT,执行过 python manage.py collectstatic --noinput,Nginx 里 location /static/ alias 路径指到 collectstatic 输出的目录。alias 路径的检查办法是用浏览器直接访问 /static/admin/css/base.css,如果 404,说明 alias 写错;如果是 200,再看页面上的静态文件 URL 是什么开头,两者要对得上。Django admin 后台样式丢了这个原因占了九成,剩下的要看项目里是否自定义了 admin 静态文件目录,有的话要确保 APP_DIRS 和 STATICFILES_DIRS 配置正确。
5.4 SQLite 写操作报 database is locked
现象:用 Django admin 保存一条记录,页面报 OperationalError: database is locked;日志里频繁出现 SQLITE_BUSY。
原因:SQLite 在同一时间只允许一个进程写文件,Gunicorn 开了多 worker,两个 worker 同时写库就互相锁死。另一个常见原因就是数据库文件权限不对,www 用户写不了 db.sqlite3。
解决:低并发项目直接把 workers 改成 1,彻底避免写竞争;保留多 worker 就迁移 MySQL。权限问题按这个命令处理:
chown -R www:www /www/wwwroot/myproject chmod 664 /www/wwwroot/myproject/db.sqlite3如果项目里还有文件上传,media 目录也需要同样的 owner 调整。改完之后在 Django shell 里跑一次简单的模型查询,确认连接正常。
5.5 访问域名报 DisallowedHost 400
现象:浏览器访问域名直接出现 400 Bad Request,页面显示 DisallowedHost 一行报错。
原因:Django 的 ALLOWED_HOSTS 校验了请求头里的 Host 字段,没有把它加入名单就拒绝。
解决:先把 settings.py 里配成 ['*'] 验证问题是否消失,确认是 Host 校验后,改成实际的域名和 IP 数组,不要长期用通配符。改完记得重启 Gunicorn,这个参数在进程启动时加载,不重启不生效。这一步也多发生在刚上传项目、还没改配置就直接访问的新手场景里。
5.6 上传大文件返回 502 Bad Gateway
现象:上传 10MB 的附件,请求发出去后 Nginx 直接返回 502,小文件正常。
原因:Nginx 默认 client_max_body_size 是 1m,请求体超过这个值,Nginx 直接断开连接,不回传给 Gunicorn,日志里会有 client intended to send too large body。
解决:在 server 块里加 client_max_body_size 20m,Nginx reload 即可。同时注意 Gunicorn 的 timeout 参数,上传处理超过 30 秒会被 Gunicorn 杀掉,按业务调大。如果项目用了前端的 chunked 分片上传,那还要检查 proxy_http_version 和 proxy_request_buffering,普通表单上传改 client_max_body_size 就够了。
6. 上线后的验证技巧:一条 curl 命令、一次压测和三个维护习惯
6.1 用一条 curl 判断链路健康
部署完成第一时间不要急着刷新浏览器,先 SSH 里 curl -I http://your_domain.com,看返回的 HTTP 头。200 说明整条链路通;502 去查 Nginx error.log;504 去看 Gunicorn 的 timeout 和数据库连接。这个习惯能帮你把问题定位到具体段,而不是在浏览器里反复刷新瞎猜。注意 curl 返回的 Server 头是 nginx,而页面内容里有没有 Django 的尾巴并不能代表它是什么时候生成的,真正判断 Django 是否响应,要在服务器内部 curl 127.0.0.1:8000。
6.2 用 ab 做一次小压测,验证 worker 数
压测工具 ab 在 httpd-tools 包里,CentOS 上 yum install -y httpd-tools 就能装。这是最快的实测手段:
ab -n 1000 -c 50 http://your_domain.com/看 Failed requests 是不是 0,Requests per second 是不是符合预期。并发 50 的情况下 2 核机器 Gunicorn 2 worker + 4 thread 一般能跑到几百 QPS 的静态响应,数据库查询多的接口会低一些。压测结果后调整 worker 数,不要一上来就堆 8 个 worker,内存会先被吃满。压测时注意 ab 的 User-Agent 会被宝塔的某些防爬插件拦截,如果页面返回 403,给 ab 加 -H "User-Agent: Mozilla/5.0" 再测。
6.3 三个维护习惯:备份、日志切割、面板安全更新
上线后我固定的三个动作是:面板计划任务里每周备份一次项目目录和数据库;日志切割每周切一次 Nginx 日志,避免 /www/wwwlogs 涨满磁盘;每月进面板设置检查一次面板版本和 Python 项目管理器插件更新,CentOS 7.9 的 yum 源不更新没关系,面板组件走的是宝塔自己的源。磁盘空间紧张时,先 df -h 看根分区,再清 /www/server/panel/logs 里堆积的安装日志。如果项目里加了定时任务,用面板的计划任务配合虚拟环境的 python 解释器执行,不要把系统 python 当作跑项目脚本的默认环境。我现在的习惯是每次改完配置顺手跑一条 curl -I 验证,十分钟就能发现大半问题,希望帮到你。真遇到状态码异常,先翻 /www/wwwlogs/ 里的 error.log,九成答案都在里面。
本文还有配套的精品资源,点击获取