You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache部署Django生产环境静态文件加载失败求助

Django + Apache部署静态文件404及MIME类型错误排查

环境信息

  • Django版本:3.0
  • Python版本:3.8
  • Web服务器:Apache2
  • 操作系统:Ubuntu

问题表现

开发模式DEBUG=True时静态文件正常加载,React内容显示正常;生产环境无论DEBUG设为True还是False,都出现以下问题:

  • JavaScript、CSS文件返回404错误
  • 文件以text/html类型返回,触发MIME类型校验错误

浏览器控制台错误日志:

GET https://www.myapp.com/static/js/main.a14a4a4b.js net::ERR_ABORTED 404 (Not Found)
Refused to execute script from 'https://www.myapp.com/static/js/main.a14a4a4b.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
Refused to apply style from 'https://www.myapp.com/static/css/main.868dfa48.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
GET https://www.myapp.com/favicon.png 404 (Not Found)
GET https://www.myapp.com/manifest.json 404 (Not Found)
Manifest: Line: 1, column: 1, Syntax error.

静态文件目录结构

/var/www/jmyapp/staticfiles:
asset-manifest.json  css  favicon.png  index.html  js  logo192.png  logo512.png  manifest.json  media  robots.txt  sitemap.xml  static

/var/www/jmyapp/staticfiles/css:
main.868dfa48.css  main.868dfa48.css.map

/var/www/jmyapp/staticfiles/js:
main.a14a4a4b.js  main.a14a4a4b.js.LICENSE.txt  main.a14a4a4b.js.map

/var/www/jmyapp/staticfiles/media:
MyAppSwap.7be50f5ecb7b614c38c9.png  eth.df265e367364f285053a1285ad8d418d.svg

/var/www/jmyapp/staticfiles/static:
css  js  media

/var/www/jmyapp/staticfiles/static/css:
main.868dfa48.css  main.868dfa48.css.map

/var/www/jmyapp/staticfiles/static/js:
main.a14a4a4b.js  main.a14a4a4b.js.LICENSE.txt  main.a14a4a4b.js.map

/var/www/jmyapp/staticfiles/static/media:
MyAppSwap.7be50f5ecb7b614c38c9.png  eth.df265e367364f285053a1285ad8d418d.svg

关键配置文件

settings.py核心配置

import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
REACT_APP_DIR = os.path.join(BASE_DIR, 'myappswap', 'dex', 'build')


STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
    os.path.join(REACT_APP_DIR, "static"),
]

DEBUG = False


TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
            REACT_APP_DIR,
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'mysite.wsgi.application'

Apache配置(default-ssl.conf)

<IfModule mod_ssl.c>
    <VirtualHost _default_:443>
        ServerName www.myapp.com
        ServerAlias myapp.com

        DocumentRoot /var/www/jmyapp

        WSGIDaemonProcess mysite python-path=/var/www/jmyapp/mysite_env/lib/python3.8/site-packages
        WSGIProcessGroup mysite
        WSGIScriptAlias / /var/www/jmyapp/mysite/wsgi.py

        Alias /robots.txt /var/www/jmyapp/staticfiles/robots.txt
        Alias /favicon.ico /var/www/jmyapp/staticfiles/favicon.png
        Alias /static/ /var/www/jmyapp/staticfiles/
        Alias /media/ /var/www/jmyapp/media/

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        SSLEngine on
        SSLProtocol -ALL +TLSv1.2
        SSLCertificateFile /etc/letsencrypt/live/myapp.com/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/myapp.com/privkey.pem

        <IfModule mod_headers.c>
            <Location "/geconame/">
                SetEnvIf Origin "http(s)?://(www\.)?(localhost|www\.myapp\.com)$" ACAO=$0
                Header set Access-Control-Allow-Origin %{ACAO}e env=ACAO
                Header merge Vary Origin
            </Location>
        </IfModule>

        <FilesMatch "\.(cgi|shtml|phtml|php)$">
            SSLOptions +StdEnvVars
        </FilesMatch>
        <Directory /usr/lib/cgi-bin>
            SSLOptions +StdEnvVars
        </Directory>

        <Directory /var/www/jmyapp>
            Require all granted
        </Directory>

        <Directory /var/www/jmyapp/staticfiles>
            Require all granted
        </Directory>

        <Directory /var/www/jmyapp/media>
            Require all granted
        </Directory>

        <Directory /var/www/jmyapp/mysite>
            <Files wsgi.py>
                Require all granted
            </Files>
        </Directory>

        BrowserMatch "MSIE [2-6]" \
            nokeepalive ssl-unclean-shutdown \
            downgrade-1.0 force-response-1.0
    </VirtualHost>
</IfModule>

Apache配置(mysite.conf)

<VirtualHost *:80>
    ServerName www.myapp.com
    ServerAlias myapp.com
    ErrorLog /var/log/apache2/error.log
    CustomLog /var/log/apache2/access.log combined

    <IfModule mod_headers.c>
        <Location "/geconame/">
            SetEnvIf Origin "http(s)?://(www\.)?(localhost|www\.myapp\.com)$" ACAO=$0
            Header set Access-Control-Allow-Origin %{ACAO}e env=ACAO
            Header merge Vary Origin
        </Location>
    </IfModule>

    WSGIDaemonProcess jmyapp python-path=/var/www/jmyapp/mysite_env/lib/python3.8/site-packages
    WSGIProcessGroup jmyapp
    WSGIScriptAlias / /var/www/jmyapp/mysite/wsgi.py

    Alias /robots.txt /var/www/jmyapp/staticfiles/robots.txt
    Alias /favicon.ico /var/www/jmyapp/staticfiles/favicon.png
    Alias /static/ /var/www/jmyapp/staticfiles/
    Alias /media/ /var/www/jmyapp/media/

    <Directory /var/www/jmyapp>
        Require all granted
    </Directory>

    <Directory /var/www/jmyapp/staticfiles>
        Require all granted
    </Directory>

    <Directory /var/www/jmyapp/media>
        Require all granted
    </Directory>

    <Directory /var/www/jmyapp/mysite>
        <Files wsgi.py>
            Require all granted
        </Files>
    </Directory>

    <Directory /var/www/jmyapp/staticfiles>
        Require all granted
    </Directory>
</VirtualHost>

URL配置(main/urls.py)

from django.urls import path
from . import views
from django.contrib.staticfiles.storage import staticfiles_storage

app_name = 'main'

urlpatterns = [
    path('', views.mappage, name='mappage'),
    path('coins/<cryptoname>', views.coinspage, name='coinspage'),
    path('prices/<number>', views.prices, name='prices'),
    path('new_prices/<number>', views.new_prices, name='new_prices'),
    path('geconame/', views.get_coin, name='coin_name'),
    path('api/crypto-data/', views.crypto_data_api, name='crypto_data_api'),
    path('api/socialinfo/', views.socialinfo, name='socialinfo'),
    path('web3/tokenPrice/', views.token_price, name='token_price'),
    path('web3/dexSwap/', views.dex_swap, name='dex_swap'),
    path('web3/broadcastTransaction/', views.broadcast_transaction, name='broadcast_transaction'),
]

URL配置(mysite/urls.py)

from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('', include('main.urls')),
    path('admin/', admin.site.urls),
    path('blog/', include('blogs.urls')),
    path('swap/', TemplateView.as_view(template_name='index.html'), name='swap'),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

package.json配置

"name": "dex",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@ant-design/icons": "^5.0.0",
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "antd": "^5.1.5",
    "axios": "^1.2.3",
    "browserify-zlib": "^0.2.0",
    "cors": "^2.8.5",
    "crypto-browserify": "^3.12.0",
    "ethers": "^5.7.2",
    "node-fetch": "^2.7.0",
    "os-browserify": "^0.3.0",
    "path-browserify": "^1.0.1",
    "querystring-es3": "^0.2.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.6.2",
    "react-scripts": "5.0.1",
    "stream-browserify": "^3.0.0",
    "url": "^0.11.3",
    "wagmi": "^0.10.11",
    "web-vitals": "^2.1.4",
    "web3": "^4.9.0",
    "yesno": "^0.4.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "eslint-config-airbnb": "^19.0.4",
    "eslint-plugin-import": "^2.29.1",
    "eslint-plugin-jsx-a11y": "^6.8.0",
    "eslint-plugin-react": "^7.34.2"
  },
  "proxy": "http://localhost:8000"
}

已尝试的解决操作

  • 修正文件权限:
    sudo chown -R www-data:www-data /var/www/jmyapp/staticfiles
    sudo chmod -R 755 /var/www/jmyapp/staticfiles
    
  • 执行collectstatic收集静态文件:
    python manage.py collectstatic
    
  • 重启Apache服务:
    sudo systemctl restart apache2
    
  • 验证静态文件存在于指定目录
  • 设置React的BrowserRouter basename为"/"

核心疑问

  1. 为何生产环境中静态文件无法被找到?
  2. 如何解决MIME类型错误?
  3. Apache配置或Django设置是否存在问题?

问题分析与解决方案

1. 静态文件404的核心原因

从目录结构看,collectstatic执行后,React构建的静态文件被存到了/var/www/jmyapp/staticfiles/static/下(比如static/js/main.a14a4a4b.js),但浏览器请求的是/static/js/main.a14a4a4b.js,而Apache的Alias /static/ /var/www/jmyapp/staticfiles/会把请求映射到/var/www/jmyapp/staticfiles/js/main.a14a4a4b.js——这个路径不存在,实际文件在staticfiles/static/js/里,所以返回404。

当Apache找不到静态文件时,会把请求转发给Django的WSGI应用,Django返回404页面(text/html类型),这就是MIME类型错误的根源。

2. 针对性修复步骤

(1)调整Django的STATICFILES_DIRS配置

当前STATICFILES_DIRS包含了REACT_APP_DIR/static,导致collectstatic时把React的静态文件复制到STATIC_ROOT/static/下。修改配置,直接把React的build目录加入STATICFILES_DIRS,这样collectstatic会把build下的所有文件(包括根目录的favicon、manifest)复制到STATIC_ROOT:

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
    REACT_APP_DIR,  # 替换原来的REACT_APP_DIR/static
]

修改后重新执行:

python manage.py collectstatic

此时staticfiles/下会直接有js、css目录,根路径的favicon、manifest也会被正确收集。

(2)修正Apache的Alias配置

确保静态文件的Alias指向正确的STATIC_ROOT:

# 保留原有配置,无需修改静态路径Alias
Alias /static/ /var/www/jmyapp/staticfiles/
# 补充根路径文件的映射
Alias /favicon.png /var/www/jmyapp/staticfiles/favicon.png
Alias /manifest.json /var/www/jmyapp/staticfiles/manifest.json

(3)移除Django urls中的static辅助配置

当DEBUG=False时,django.conf.urls.static.static()不会生效,静态文件完全由Apache处理,可从mysite/urls.py中移除这部分:

urlpatterns = [
    path('', include('main.urls')),
    path('admin/', admin.site.urls),
    path('blog/', include('blogs.urls')),
    path('swap/', TemplateView.as_view(template_name='index.html'), name='swap'),
]

(4)重启Apache服务

sudo systemctl restart apache2

3. 验证修复效果

  • 检查浏览器控制台,确认静态文件请求返回200
  • 确认MIME类型错误消失,JS、CSS正常加载
  • 验证favicon.png和manifest.json可正常访问

内容的提问来源于stack exchange,提问作者Brapu Coin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 17:42:01