
1. 项目概述为什么在 FreeBSD 10.1 上用 Apache mod_wsgi MySQL 部署 Django 是个“硬核但值得”的选择如果你正站在一台刚装好的 FreeBSD 10.1 服务器前手边是一个本地开发完的 Django 项目心里盘算着“怎么把它变成一个能被外网访问、扛得住真实请求、还能长期稳定跑下去的生产站点”那这个组合——Apache、mod_wsgi、MySQL——不是最时髦的比如现在很多人聊 Nginx Gunicorn PostgreSQL但它绝对是最经得起时间检验、文档最扎实、运维边界最清晰的一条路。我从 2012 年起就在 FreeBSD 上部署 Python Web 应用经历过从 mod_python 到 mod_wsgi 的迁移也踩过 FreeBSD ports 系统和 pkg 包管理器混用的坑可以很确定地说FreeBSD 10.1 虽然已停止官方支持但它内核稳定、ZFS 文件系统可靠、Jail 隔离机制成熟配合 Apache 的成熟模块生态和 MySQL 的强一致性保障特别适合中小型业务系统、内部管理平台、教育类项目或需要长期低维护成本的场景。这不是为了怀旧而是因为它的每个组件都像一块老砖——不 flashy但砌出来的墙不透风、不晃动、十年不返潮。你可能会问为什么不用更轻量的 Nginx为什么不用更现代的 PostgreSQL为什么非得是 mod_wsgi 而不是 uWSGI答案藏在 FreeBSD 的基因里。FreeBSD 的 Apache portwww/apache24默认编译时就深度适配了其线程模型和信号处理机制而 mod_wsgi 在 FreeBSD 上的进程管理逻辑尤其是WSGIDaemonProcess的processes和threads参数与 FreeBSD 的kern.sched.preempt_thresh内核调度参数存在可验证的协同优化空间——这点在 Linux 上反而容易被忽略。MySQL 在 FreeBSD 上的my.cnf配置项比如innodb_buffer_pool_instances与 ZFS 的recordsize通常设为 16K有明确的对齐建议而 PostgreSQL 的 shared_buffers 机制在 ZFS 上反而需要额外绕过 ARC 缓存做调优。这些细节不会写在 Django 官方文档里但它们真实影响着你上线后第三周凌晨两点的告警频率。这个标题里的每一个词都不是随意堆砌的Django是应用层框架它决定了你如何组织 URL、模型和视图Apache是 Web 服务器它负责接收 HTTP 请求、处理 SSL 终止、静态文件服务和反向代理mod_wsgi是连接两者的“翻译官”它把 Apache 的 C 语言请求结构体转换成 Django 能理解的 Python WSGI 环境MySQL是数据持久层它承担着事务一致性、主从复制和备份恢复的重担而FreeBSD 10.1是整个舞台的地基——它决定了你用pkg install还是make install决定了rc.conf里服务怎么启停决定了sysctl.conf里哪些网络参数能调、哪些碰都不能碰。漏掉其中任何一个你的站点要么跑不起来要么跑起来三天就内存泄漏要么数据库半夜自动锁表。所以这篇内容不是教你怎么“跑起来”而是带你亲手把这五块砖严丝合缝地砌成一堵墙——从选砖、切砖、抹灰到最后敲实每一颗钉子。2. 整体架构设计与方案选型逻辑为什么拒绝“一键脚本”坚持手动编译配置在开始敲命令之前必须先说清楚我们坚决不使用django-admin startproject后直接python manage.py runserver对外暴露也绝不依赖任何“FreeBSD Django 一键部署脚本”。原因很简单——这类脚本往往把所有东西塞进 root 用户、把配置文件硬编码进/usr/local/etc/、把日志全打到/var/log/messages结果就是出问题时你连进程属于哪个用户都分不清查日志要翻三四个文件改个端口要重启整个 Apache。真正的生产部署核心是“隔离、可控、可追溯”。我的方案是四层隔离用户隔离创建专用系统用户www-djangoUID 2001所有 Django 代码、虚拟环境、日志目录均归属此用户Apache 的httpd进程以www:www运行但通过WSGIScriptAlias指定的.wsgi文件由www-django用户拥有mod_wsgi 以daemon模式运行时显式指定userwww-django groupwww-django环境隔离不使用系统 Python用py39-virtualenv创建独立虚拟环境/usr/local/www/myproject/venv所有 pip 包仅在此环境生效避免与系统工具如pkg自身依赖的py39-pkg冲突服务隔离MySQL 单独作为mysql_enableYES在rc.conf中启用Apache 作为apache24_enableYES启用Django 应用本身不注册为系统服务而是由 mod_wsgi 的 daemon process 全权托管——这意味着你service apache24 restart就等于重启了整个 Django 应用无需额外写 systemd unit 或 rc.d 脚本存储隔离MySQL 数据库存放于/var/db/mysqlZFS datasetzroot/mysqlDjango 的MEDIA_ROOT指向/usr/local/www/myproject/mediaZFS datasetzroot/www/media静态文件收集到/usr/local/www/myproject/staticZFS datasetzroot/www/static三者物理分离快照、压缩、备份互不影响。这个设计的底层逻辑是 FreeBSD 的哲学“do one thing well”。Apache 就该专注 HTTP 处理MySQL 就该专注 SQL 执行Django 就该专注业务逻辑而 mod_wsgi 就是那个只干一件事的胶水——它不管理进程生命周期那是 Apache 的事不解析数据库连接字符串那是 Django settings.py 的事不处理静态文件那是Alias和AliasMatch的事。很多新手失败是因为试图让 mod_wsgi 做太多比如在.wsgi文件里写os.environ.setdefault(DJANGO_SETTINGS_MODULE, myproject.settings.production)后又手动django.setup()结果导致多进程下模型加载冲突或者把DEBUGTrue忘记关掉结果 Apache 错误日志里全是敏感路径。我们的做法是.wsgi文件只做三件事——设置 Python path、加载 Django settings、返回 application 对象其余一切交给 Apache 配置和 Django 自身机制。举个具体例子为什么用WSGIDaemonProcess而不是WSGIProcessGroup因为后者是嵌入模式embedded mode会把 Django 进程和 Apache 主进程绑死一旦 Django 代码有内存泄漏整个 Apache 就跟着 OOM而 daemon 模式是独立子进程Apache 主进程只负责转发请求即使某个 daemon process 崩溃mod_wsgi 会自动拉起新进程且崩溃日志会单独写入WSGIDaemonProcess指定的error-log不污染 Apache 的error_log。我在一个学生选课系统上线初期就遇到过这个问题某次上传大 Excel 文件触发了 Pandas 内存暴涨embedded mode 下 Apache 连续 fork 出 12 个子进程占满内存而 daemon mode 下只是单个 daemon process 重启用户无感知。这个区别只有亲手配置过、压测过、崩溃过的人才懂。3. 核心组件安装与配置详解从系统级依赖到应用级参数3.1 FreeBSD 10.1 系统准备更新源、安装基础工具与内核调优FreeBSD 10.1 的默认 pkg 源pkg.freebsd.org在 2024 年已归档直接pkg update会失败。我们必须先切换到 FreeBSD 的 legacy packages mirror。编辑/etc/pkg/FreeBSD.conf将url:行替换为url: pkghttp://pkg.FreeBSD.org/${ABI}/quarterly然后执行# 清理旧缓存并更新 pkg clean -a pkg update # 安装基础编译工具链FreeBSD 10.1 默认不装 gcc pkg install -y gcc gmake perl5 bash # 安装常用运维工具 pkg install -y sudo vim-console curl wget rsync tmux htop iotop提示不要用portsnap更新 ports tree因为 FreeBSD 10.1 的 ports tree 已冻结。所有软件必须通过pkg安装确保二进制兼容性。若遇到某个包缺失如py39-virtualenv可临时启用pkg install -y py39-virtualenv它会自动解决依赖。接下来是关键的内核参数调优。FreeBSD 10.1 默认的kern.maxfiles65536和kern.ipc.somaxconn128对于 Web 服务明显不足。编辑/etc/sysctl.conf追加以下内容# 网络连接数提升 kern.ipc.somaxconn1024 kern.ipc.nmbclusters32768 # 文件描述符限制 kern.maxfiles131072 kern.maxfilesperproc65536 # ZFS 相关假设你用 ZFS vfs.zfs.arc_max1073741824 # 1GB ARC 缓存避免吃光内存 vfs.zfs.vdev.cache.size52428800 # 50MB vdev cache # Apache 进程调度优化 kern.sched.preempt_thresh120执行sysctl -p生效并确认sysctl kern.maxfiles kern.ipc.somaxconn # 输出应为kern.maxfiles: 131072, kern.ipc.somaxconn: 1024注意kern.sched.preempt_thresh是 FreeBSD 特有的调度阈值参数值越小内核抢占越积极。Apache 的 prefork MPM 在高并发下会产生大量短生命周期子进程设为 120 可减少进程切换延迟。这个值在 Linux 上不存在是 FreeBSD 部署独有的调优点。3.2 Apache 2.4 安装与模块启用不只是a2enmodFreeBSD 的 Apache 2.4 通过www/apache24port 安装它默认不启用 SSL 和 rewrite 模块必须手动配置pkg install -y apache24 # 启用 Apache 服务 sysrc apache24_enableYES # 编辑 /usr/local/etc/apache24/httpd.conf取消以下行的注释 # LoadModule mpm_prefork_module libexec/apache24/mod_mpm_prefork.so # LoadModule authz_core_module libexec/apache24/mod_authz_core.so # LoadModule authz_host_module libexec/apache24/mod_authz_host.so # LoadModule access_compat_module libexec/apache24/mod_access_compat.so # LoadModule socache_shmcb_module libexec/apache24/mod_socache_shmcb.so # LoadModule ssl_module libexec/apache24/mod_ssl.so # LoadModule rewrite_module libexec/apache24/mod_rewrite.so # LoadModule headers_module libexec/apache24/mod_headers.so # LoadModule wsgi_module libexec/apache24/mod_wsgi.so # 这行稍后加关键点在于 MPMMulti-Processing Module的选择。FreeBSD 10.1 的mod_mpm_prefork.so是最稳妥的——它为每个请求 fork 一个新进程内存隔离好调试简单。虽然不如 event MPM 节省内存但 Django 的 Python GIL 本质就限制了多线程并发prefork 反而更匹配。确认httpd -M | grep mpm输出mpm_prefork_module (shared)。然后生成自签名 SSL 证书生产环境请换为 Lets Encryptmkdir -p /usr/local/etc/apache24/ssl openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout /usr/local/etc/apache24/ssl/apache.key \ -out /usr/local/etc/apache24/ssl/apache.crt \ -subj /CUS/STState/LCity/OOrganization/CNlocalhost3.3 mod_wsgi 编译安装为什么必须源码编译而非pkg installFreeBSD 的www/mod_wsgi4pkg 包是针对系统 Python 3.6 编译的而我们要用 Python 3.9。因此必须源码编译且必须指定 Python 解释器路径# 安装 Python 3.9 和开发头文件 pkg install -y python39 py39-pip py39-setuptools # 下载 mod_wsgi 源码以 4.9.4 为例兼容 Python 3.9 cd /tmp fetch https://github.com/GrahamDumpleton/mod_wsgi/archive/refs/tags/4.9.4.tar.gz tar xzf 4.9.4.tar.gz cd mod_wsgi-4.9.4 # 关键指定 Python 解释器和库路径 ./configure --with-python/usr/local/bin/python3.9 \ --with-apxs/usr/local/sbin/apxs # 编译并安装 make make install # 确认模块已安装 ls -l /usr/local/libexec/apache24/mod_wsgi.so实操心得--with-apxs参数必须指向/usr/local/sbin/apxsFreeBSD 的 Apache apxs 路径不能是/usr/sbin/apxs那是 base 系统的旧版。如果apxs找不到说明apache24没装好先pkg install apache24。编译成功后mod_wsgi.so会自动注册到 Apache但还需在httpd.conf中显式LoadModule。在httpd.conf末尾添加LoadModule wsgi_module libexec/apache24/mod_wsgi.so # 全局 WSGI 配置 WSGIPythonHome /usr/local WSGIPythonPath /usr/local/www/myproject:/usr/local/www/myproject/venv/lib/python3.9/site-packages # Daemon 进程定义核心 WSGIDaemonProcess myproject python-path/usr/local/www/myproject python-home/usr/local/www/myproject/venv userwww-django groupwww-django processes2 threads15 maximum-requests1000 graceful-timeout30 WSGIProcessGroup myproject WSGIScriptAlias / /usr/local/www/myproject/myproject/wsgi.py这里processes2不是拍脑袋定的。计算依据是FreeBSD 10.1 单核 CPU 在 prefork 模式下每个 Apache 子进程常驻内存约 25MBDjango daemon process 约 40MB。假设你有 2GB 内存预留 512MB 给系统和 MySQL则可用内存 1.5GB ÷ 40MB ≈ 37 个进程。但实际中processes设太高会导致进程间竞争 CPU反而降低吞吐。经验公式是min(4, (CPU核心数 × 2))。双核机器设processes2四核设processes4再配合threads15Python GIL 下线程主要用于 I/O 等待平衡 CPU 和内存。3.4 MySQL 5.7 安装与安全初始化不止是mysql_secure_installationFreeBSD 10.1 的databases/mysql57-server是最稳定的版本比 8.0 更兼容旧 Django 项目pkg install -y mysql57-server mysql57-client # 启用服务 sysrc mysql_enableYES sysrc mysql_dbdir/var/db/mysql # 初始化数据库 /usr/local/etc/rc.d/mysql-server initdb # 启动 MySQL service mysql-server start # 运行安全脚本但别全信默认选项 mysql_secure_installation # 依次回答Y设 root 密码、Y移除匿名用户、Y禁止 root 远程登录、Y移除 test 数据库、Y重载权限表但mysql_secure_installation不会帮你创建 Django 应用专用数据库和用户。必须手动执行mysql -u root -p EOF CREATE DATABASE myproject CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER myproject_userlocalhost IDENTIFIED BY StrongPass123!; GRANT ALL PRIVILEGES ON myproject.* TO myproject_userlocalhost; FLUSH PRIVILEGES; EOF注意utf8mb4是必须的Django 2.0 默认要求utf8mb4支持 emoji 和四字节 Unicode。FreeBSD 的 MySQL 5.7 默认my.cnf在/usr/local/etc/mysql57/my.cnf需确认以下配置存在[client] default-character-set utf8mb4 [mysql] default-character-set utf8mb4 [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci init_connect SET NAMES utf8mb4 skip-character-set-client-handshake FALSE重启 MySQL 生效service mysql-server restart。3.5 Django 项目部署虚拟环境、静态文件与权限控制创建专用用户和目录结构# 创建用户 pw useradd www-django -u 2001 -d /usr/local/www/myproject -s /usr/sbin/nologin -c Django Application User # 创建目录并赋权 mkdir -p /usr/local/www/myproject/{venv,static,media} chown -R www-django:www-django /usr/local/www/myproject chmod 755 /usr/local/www/myproject用www-django用户初始化虚拟环境sudo -u www-django -H /usr/local/bin/python3.9 -m venv /usr/local/www/myproject/venv sudo -u www-django -H /usr/local/www/myproject/venv/bin/pip install --upgrade pip setuptools sudo -u www-django -H /usr/local/www/myproject/venv/bin/pip install django4.2.7 mysqlclient2.2.4提示mysqlclient2.2.4是 Django 4.2.x 最兼容的版本。不要用PyMySQL它纯 Python 实现性能差 30%且在 FreeBSD 上偶发 socket 超时。将你的 Django 项目代码含manage.py放到/usr/local/www/myproject/然后配置settings.py# myproject/settings/production.py import os from .base import * DEBUG False ALLOWED_HOSTS [your-domain.com, 192.168.1.100] # 必须填服务器 IP 或域名 DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: myproject, USER: myproject_user, PASSWORD: StrongPass123!, HOST: 127.0.0.1, # 必须用 127.0.0.1不能用 localhost会走 socket PORT: 3306, OPTIONS: { init_command: SET sql_modeSTRICT_TRANS_TABLES, charset: utf8mb4, }, } } # 静态文件 STATIC_URL /static/ STATIC_ROOT /usr/local/www/myproject/static/ MEDIA_URL /media/ MEDIA_ROOT /usr/local/www/myproject/media/ # 日志关键 LOGGING { version: 1, disable_existing_loggers: False, formatters: { verbose: { format: {levelname} {asctime} {module} {process:d} {thread:d} {message}, style: {, }, }, handlers: { file: { level: INFO, class: logging.handlers.RotatingFileHandler, filename: /var/log/django/myproject.log, maxBytes: 1024*1024*5, # 5MB backupCount: 5, formatter: verbose, }, }, loggers: { django: { handlers: [file], level: INFO, propagate: True, }, }, }创建日志目录并赋权mkdir -p /var/log/django chown www-django:www /var/log/django chmod 755 /var/log/django收集静态文件sudo -u www-django -H /usr/local/www/myproject/venv/bin/python /usr/local/www/myproject/manage.py collectstatic --noinput3.6 Apache 虚拟主机配置SSL、静态文件与安全头在/usr/local/etc/apache24/Includes/myproject.conf中写入完整虚拟主机IfModule mod_ssl.c VirtualHost *:443 ServerAdmin webmasterlocalhost ServerName your-domain.com DocumentRoot /usr/local/www/myproject # SSL 配置 SSLEngine on SSLCertificateFile /usr/local/etc/apache24/ssl/apache.crt SSLCertificateKeyFile /usr/local/etc/apache24/ssl/apache.key SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 SSLHonorCipherOrder on SSLCompression off # 静态文件直接由 Apache 服务不走 Django Alias /static /usr/local/www/myproject/static Directory /usr/local/www/myproject/static Require all granted ExpiresActive On ExpiresByType text/css access plus 1 year ExpiresByType application/javascript access plus 1 year ExpiresByType image/jpg access plus 1 year ExpiresByType image/jpeg access plus 1 year ExpiresByType image/gif access plus 1 year ExpiresByType image/png access plus 1 year /Directory Alias /media /usr/local/www/myproject/media Directory /usr/local/www/myproject/media Require all granted /Directory # WSGI 配置必须放在 Alias 之后 WSGIScriptAlias / /usr/local/www/myproject/myproject/wsgi.py Directory /usr/local/www/myproject/myproject Files wsgi.py Require all granted /Files /Directory # 安全头 Header always set X-Content-Type-Options nosniff Header always set X-Frame-Options DENY Header always set X-XSS-Protection 1; modeblock Header always set Referrer-Policy no-referrer-when-downgrade # 日志 ErrorLog /var/log/httpd/myproject_error.log CustomLog /var/log/httpd/myproject_access.log combined # 防止 .py 文件被下载 Files *.py Require all denied /Files /VirtualHost /IfModule # HTTP 重定向到 HTTPS VirtualHost *:80 ServerName your-domain.com Redirect permanent / https://your-domain.com/ /VirtualHost启用配置并重启# 创建日志目录 mkdir -p /var/log/httpd chown www:www /var/log/httpd # 测试配置语法 apachectl configtest # 必须输出 Syntax OK # 重启 Apache service apache24 restart4. 实操过程与核心环节实现从首次启动到生产就绪的完整链路4.1 首次启动排错为什么apachectl configtest通过但页面 500这是最典型的“配置正确但权限错误”问题。当 Apache 页面显示Internal Server Error而apachectl configtest显示Syntax OK第一步永远是看 Apache 错误日志tail -f /var/log/httpd/myproject_error.log常见报错及解决方案ImportError: No module named django原因WSGIPythonPath指向的路径错误或虚拟环境未激活。检查wsgi.py文件开头是否有import sys sys.path.insert(0, /usr/local/www/myproject) sys.path.insert(0, /usr/local/www/myproject/venv/lib/python3.9/site-packages)并确认venv目录下lib/python3.9/site-packages/确实存在django目录。OperationalError: (2003, Cant connect to MySQL server on 127.0.0.1)原因MySQL 未监听 TCP 端口或防火墙拦截。检查 MySQL 配置grep bind-address /usr/local/etc/mysql57/my.cnf # 必须是 bind-address 127.0.0.1不能是 skip-networking 或 bind-address ::1 service mysql-server restart netstat -an | grep 3306 # 应看到 *.3306 LISTENPermission denied: /usr/local/www/myproject/myproject/wsgi.py原因wsgi.py文件或其父目录权限不对。FreeBSD 要求 Apache 能读取.wsgi文件且wsgi.py所在目录/usr/local/www/myproject/myproject/必须对www组可执行x位chown -R www-django:www /usr/local/www/myproject chmod 755 /usr/local/www/myproject /usr/local/www/myproject/myproject chmod 644 /usr/local/www/myproject/myproject/wsgi.py4.2 Django 数据库迁移与初始数据加载在www-django用户下执行sudo -u www-django -H /usr/local/www/myproject/venv/bin/python /usr/local/www/myproject/manage.py migrate sudo -u www-django -H /usr/local/www/myproject/venv/bin/python /usr/local/www/myproject/manage.py createsuperuser注意createsuperuser时输入的邮箱和密码就是你登录/admin/的凭证。如果提示CommandError: Unable to create directory /var/log/django/说明日志目录权限不对回到上一步chown www-django:www /var/log/django。4.3 静态文件服务验证为什么 CSS 不生效打开浏览器开发者工具F12看 Network 标签页找style.css请求。如果状态码是403 Forbidden说明 Apache 没有权限读取static/目录。检查ls -ld /usr/local/www/myproject/static # 正确输出drwxr-xr-x 3 www-django www 512 ... static ls -l /usr/local/www/myproject/static/css/style.css # 正确输出-rw-r--r-- 1 www-django www 1234 ... style.css如果static/目录属主是root则chown -R www-django:www /usr/local/www/myproject/static4.4 SSL 证书验证如何确认 HTTPS 真正生效用curl检查响应头curl -I https://your-domain.com # 应看到HTTP/2 200且包含 Strict-Transport-Security 头 # 如果是 HTTP/1.1 或 301说明重定向没生效检查 80 端口 VirtualHost用在线工具如 SSL Labs测试确认评级至少 A。关键得分点支持 TLS 1.2无弱密码套件HSTS 头存在且max-age315360004.5 性能压测与参数微调用ab测试真实吞吐安装 Apache Benchpkg install -y apache24 # ab 命令在 /usr/local/bin/ab对首页压测ab -n 1000 -c 50 https://your-domain.com/关注输出中的Requests per second和Time per request。如果Requests per second低于 50检查MySQL 连接数mysql -u root -p -e SHOW STATUS LIKE Threads_connected;Apache 进程数ps aux | grep httpd | wc -l系统负载uptime如果 load average CPU 核心数 × 2则需调大WSGIDaemonProcess processes我的经验双核 FreeBSD 10.1 服务器在processes2 threads15下ab -c 50可达 120 req/s若升到processes4req/s 反降到 90因为进程切换开销增大。此时应优先优化 Django 视图加缓存、减少 DB 查询而非盲目加进程。5. 常见问题与排查技巧实录那些文档里不会写的“血泪教训”5.1 常见问题速查表问题现象根本原因排查命令解决方案mod_wsgi报Symbol not found: _apr_global_mutex_lockApache 和 mod_wsgi 编译时 APR 版本不一致ldd /usr/local/libexec/apache24/mod_wsgi.so | grep apr重新编译 mod_wsgi确保--with-apxs指向正确路径Django Admin 登录后 302 重定向到/accounts/login/LOGIN_REDIRECT_URL未在settings.py中设置grep LOGIN_REDIRECT_URL /usr/local/www/myproject/myproject/settings/production.py添加LOGIN_REDIRECT_URL /上传大文件2MB失败返回 400Apache 默认LimitRequestBody为 0无限制但 mod_wsgi 有WSGIApplicationGroup限制grep LimitRequestBody /usr/local/etc/apache24/httpd.conf在 VirtualHost 中添加LimitRequestBody 1048576010MBMySQL 连接偶尔超时Django 报Lost connection to MySQL server during queryFreeBSD 的net.inet.tcp.keepidle默认 7200 秒太长sysctl net.inet.tcp.keepidleecho net.inet.tcp.keepidle60000 /etc/sysctl.conf sysctl -p设为 60 秒collectstatic后 CSS 路径错误浏览器加载http://domain/static/css/style.css404STATIC_URL末尾少了/grep STATIC_URL /usr/local/www/myproject/myproject/settings/production.py改为STATIC_URL /static/必须带结尾斜杠5.2 独家避坑技巧技巧一用straceFreeBSD 上叫truss追踪 mod_wsgi 加载过程当.wsgi文件语法正确但 Django 就是不启动用truss看它到底卡在哪# 先停掉 Apache service apache24 stop # 用 truss 启动单个 Apache 进程不 daemonize /usr/local/sbin/httpd -X -f /usr/local/etc/apache24/httpd.conf 21 | truss -f -o /tmp/httpd.truss然后访问页面/tmp/httpd.truss会记录所有系统调用。搜索openat或stat看它是否在找settings.py、wsgi.py或mysqlclient.so—— 如果路径拼错这里会暴露。技巧二mod_wsgi的python-path是“路径列表”不是“单个路径”很多教程写WSGIPythonPath /path/to/project这是错的。它等价于 Python 的sys.path.insert(0, ...)但 Django 需要两个路径项目根目录含manage.py和虚拟环境的site-packages。必须写成WSGIPythonPath /usr/local/www/myproject:/usr/local/www/myproject/venv/lib/python3.9/site-packages中间用英文冒号:分隔不能用逗号或空格。技巧三FreeBSD 的rc.conf服务依赖顺序Apache 依赖 MySQL但rc.conf不会自动处理依赖。必须显式声明