Статические файлы Django не загружаются корректно в производстве с Apache
У меня возникли проблемы с обслуживанием статических файлов в приложении Django, развернутом на Apache. Несмотря на многочисленные руководства и предложения, я все еще сталкиваюсь с 404 ошибками для моих статических файлов. Ниже приведены соответствующие детали моей установки и проблемы, с которыми я сталкиваюсь.
Окружение:
Django Версия: 3.0 Версия Python: 3.8 Веб-сервер: Apache2 ОС: Ubuntu Описание проблемы:
В режиме разработки с DEBUG=True статические файлы загружаются корректно, и я могу видеть свою сборку React. Однако в режиме производства с DEBUG=False (или даже с Debug=True) я сталкиваюсь со следующими ошибками:
Ошибки404 для файлов JavaScript и CSS Ошибки типа MIME, указывающие на то, что файлы обслуживаются как text/html вместо их правильных типов Ошибки в консоли браузера:
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.
Структура каталога:
Вот структура моего каталога staticfiles:
/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>
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>
Конфигурация URLs:
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'),
]
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
Убедились, что файлы существуют в правильных каталогах.
Выпуск:
Даже после всех этих шагов статические файлы все еще не обслуживаются корректно в продакшене. Браузер пытается загрузить их с URL /static/, но они не найдены, что приводит к 404 ошибкам и проблемам с типом MIME. Я даже установил
<BrowserRouter basename="/">
Вопросы:
Почему статические файлы не обнаруживаются в производстве? Как решить проблему с типом MIME? Может быть, что-то не так с конфигурацией Apache или настройками Django? Любая помощь будет очень признательна.