Django/Vue部署Heroku生产环境静态文件404无法加载问题求助
按以下优先级逐一核对,绝大多数同类问题都是配置遗漏或Vue打包路径问题导致:
1. 核对核心静态配置项
三个配置项缺一不可,错一个whitenoise就无法在生产环境找到资源:
STATIC_URL固定设置为/static/,不要自定义特殊前缀STATIC_ROOT必须显式配置,推荐值为BASE_DIR / 'staticfiles'——这是Heroku默认识别的collectstatic输出目录,也是whitenoise默认扫描静态资源的根路径STATICFILES_DIRS要正确指向Vue构建生成的dist目录,路径拼接不要出错,参考写法:
STATICFILES_DIRS = [ BASE_DIR / "frontend/dist", # 替换为你项目中dist目录的实际相对路径 ]
注意:绝对不要把STATIC_ROOT和STATICFILES_DIRS设为同一路径,collectstatic执行时会清空STATIC_ROOT下的所有内容,路径重复会直接把dist目录里的静态文件删空
2. 检查collectstatic的实际执行结果
翻Heroku的部署日志,找到collectstatic的执行输出,重点看两个信息:
- 日志里是否打印了类似
XXX static files copied to '/app/staticfiles'的提示,统计的文件数要和你本地dist目录下css、js、字体、图片等静态资源的总数匹配 - 如果显示0个文件被复制,要么是
STATICFILES_DIRS的路径配错了(比如多套了一层目录、相对路径计算错误,Heroku部署时的工作目录是manage.py所在的项目根目录),要么是提交到git的dist目录是空的——很多人本地跑了npm run build生成了dist,但提交代码时漏了dist内的文件,只推了空文件夹上去,Heroku拉到的代码里根本没有静态资源,collectstatic自然搜不到内容。
本地可以提前验证:在项目根目录手动执行python manage.py collectstatic --noinput,执行完打开staticfiles目录看有没有Vue打包生成的js、css文件,如果本地跑完这个目录是空的,Heroku上执行结果必然也是空的。
3. 修正Vue打包的公共路径配置
这是首次用Vue对接Django最容易踩的坑,也是和纯Django项目最大的差异点:
Vue默认打包生成的index.html中,静态资源引用路径是根路径,比如/js/app.xxx.js、/css/app.xxx.css,但Django的静态资源默认是挂载在/static/前缀下的,如果不改Vue配置,最终发起的静态请求路径会缺失/static前缀,直接返回404。
解决方法:在Vue项目根目录新建或修改vue.config.js,添加公共路径配置:
module.exports = { publicPath: '/static/', outputDir: 'dist', indexPath: 'index.html' }
改完重新本地执行npm run build,检查dist下index.html里的资源引用是不是变成了/static/js/xxx.js这类带/static前缀的路径,确认无误后把新生成的dist内容提交到git再部署。
4. 核对whitenoise的配置顺序
whitenoise中间件的摆放顺序有严格要求,必须放在Django自带的SecurityMiddleware之后,其余所有中间件之前,顺序错了静态请求根本不会走到whitenoise处理逻辑:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', # 固定放在这个位置,不要往下挪 'django.contrib.sessions.middleware.SessionMiddleware', # 其余中间件按原有顺序放在后面 ]
如果开启了whitenoise压缩功能,确认静态文件存储配置正确:
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
注意:这个存储类会自动给静态文件拼接内容hash后缀,必须先改完Vue的publicPath配置重新打包,再启用这个存储类,否则index.html里引用的路径和实际生成的带hash的文件名不匹配,还是会报404
本地无法复现的原因
本地用runserver启动时,哪怕设置了DEBUG=False,Django开发服务器还是会做一层兜底,直接从STATICFILES_DIRS里读取静态资源返回,不会完全走生产环境whitenoise读取STATIC_ROOT的逻辑,所以本地复现不了是正常的。想在本地复现生产环境行为,只要手动执行完collectstatic之后,用gunicorn这类生产WSGI服务器启动项目就行。
内容的提问来源于stack exchange,提问作者Mike Johnson Jr

