- Created nginx-smartsoltech-fixed.conf with proper SSL and CSRF support - Preserved existing SSL certificates from /etc/letsencrypt/live/www.smartsoltech.kr/ - Added X-Forwarded-Host header (critical for Django CSRF validation) - Fixed location block order (static/media before /) - Implemented proper HTTP→HTTPS and www→non-www redirects - Updated CSRF_FIX.md with comprehensive troubleshooting guide
564 lines
18 KiB
Markdown
564 lines
18 KiB
Markdown
# 🔒 Исправление ошибки CSRF 403 в админке через Nginx
|
||
|
||
## ❌ Проблема
|
||
```
|
||
Ошибка доступа (403)
|
||
Ошибка проверки CSRF. Запрос отклонён.
|
||
```
|
||
|
||
Появляется при входе в админку через Nginx (https://smartsoltech.kr/admin/)
|
||
|
||
## 💡 Причина
|
||
|
||
Django проверяет источник запроса (origin) для защиты от CSRF атак. Когда запрос идёт через Nginx:
|
||
1. Django видит заголовок `Origin: https://smartsoltech.kr`
|
||
2. Но в `CSRF_TRUSTED_ORIGINS` нет этого домена
|
||
3. Nginx не передаёт заголовок `X-Forwarded-Host` (критично для CSRF)
|
||
4. Django отклоняет запрос как небезопасный
|
||
|
||
**Ваша конфигурация SSL:** Сертификаты находятся в `/etc/letsencrypt/live/www.smartsoltech.kr/`
|
||
|
||
---
|
||
|
||
## ✅ Решение (2 простых шага)
|
||
|
||
### Шаг 1: Обновить Nginx конфигурацию
|
||
|
||
```bash
|
||
# На сервере
|
||
cd /opt/smartsoltech_site
|
||
git pull origin master
|
||
|
||
# Применить исправленную конфигурацию (сохраняет ваши SSL сертификаты)
|
||
sudo cp nginx-smartsoltech-fixed.conf /etc/nginx/sites-available/smartsoltech
|
||
|
||
# Проверить синтаксис
|
||
sudo nginx -t
|
||
|
||
# Если OK - перезагрузить Nginx
|
||
sudo systemctl reload nginx
|
||
```
|
||
|
||
**Что исправили в Nginx:**
|
||
- ✅ Добавлен `X-Forwarded-Host: $host` (критично для CSRF)
|
||
- ✅ Сохранены ваши SSL сертификаты из `/etc/letsencrypt/live/www.smartsoltech.kr/`
|
||
- ✅ Правильный порядок location блоков (/static/ перед /)
|
||
- ✅ Redirect www → non-www
|
||
- ✅ Redirect HTTP → HTTPS
|
||
|
||
### Шаг 2: Обновить CSRF_TRUSTED_ORIGINS в .env
|
||
|
||
```bash
|
||
# Редактировать .env
|
||
cd /opt/smartsoltech_site
|
||
nano .env
|
||
```
|
||
|
||
**НАЙТИ строку:**
|
||
```env
|
||
CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr
|
||
```
|
||
|
||
**ЗАМЕНИТЬ НА:**
|
||
```env
|
||
CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr,https://www.smartsoltech.kr
|
||
```
|
||
|
||
**Сохранить:** Ctrl+O, Enter, Ctrl+X
|
||
|
||
**Перезапустить Django:**
|
||
```bash
|
||
docker restart django_app
|
||
|
||
# Проверить что контейнер запущен
|
||
docker ps | grep django_app
|
||
```
|
||
|
||
---
|
||
|
||
### Вариант 2: Ручное исправление
|
||
|
||
#### Шаг 1: Исправить Nginx конфиг
|
||
|
||
```bash
|
||
sudo nano /etc/nginx/sites-available/smartsoltech
|
||
```
|
||
|
||
**Найти секцию location / и добавить заголовки:**
|
||
|
||
```nginx
|
||
location / {
|
||
proxy_pass http://localhost: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;
|
||
proxy_set_header X-Forwarded-Host $host; # ДОБАВИТЬ
|
||
proxy_set_header X-Forwarded-Port $server_port; # ДОБАВИТЬ
|
||
proxy_redirect off;
|
||
proxy_buffering off;
|
||
...
|
||
}
|
||
```
|
||
|
||
**Проверить и перезагрузить:**
|
||
```bash
|
||
sudo nginx -t
|
||
sudo systemctl reload nginx
|
||
```
|
||
|
||
#### Шаг 2: Исправить .env файл
|
||
|
||
```bash
|
||
cd /opt/smartsoltech_site
|
||
nano .env
|
||
```
|
||
|
||
**Найти строку:**
|
||
```env
|
||
CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr
|
||
```
|
||
|
||
**Заменить на:**
|
||
```env
|
||
CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr
|
||
```
|
||
|
||
**Сохранить:** Ctrl+O, Enter, Ctrl+X
|
||
|
||
#### Шаг 3: Перезапустить Django
|
||
|
||
```bash
|
||
docker compose restart django_app
|
||
|
||
# Проверить логи
|
||
docker logs django_app --tail 50
|
||
```
|
||
|
||
---
|
||
|
||
## 🧪 Проверка работы
|
||
|
||
### Тест 1: Проверка переменных окружения
|
||
|
||
```bash
|
||
# Убедиться что Django видит правильный CSRF_TRUSTED_ORIGINS
|
||
docker exec django_app env | grep CSRF_TRUSTED_ORIGINS
|
||
|
||
# Должен вывести:
|
||
# CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr,https://www.smartsoltech.kr
|
||
```
|
||
|
||
### Тест 2: Проверка входа в админку
|
||
|
||
1. Открыть браузер: **https://smartsoltech.kr/admin/**
|
||
2. Ввести логин и пароль
|
||
3. Нажать "Войти"
|
||
4. ✅ **Должен успешно войти без ошибки 403**
|
||
|
||
### Тест 3: Проверка Nginx заголовков
|
||
|
||
```bash
|
||
# Проверить что Nginx передаёт все нужные заголовки
|
||
curl -v https://smartsoltech.kr/admin/ 2>&1 | grep -i forward
|
||
|
||
# Должны быть:
|
||
# X-Forwarded-Proto: https
|
||
# X-Forwarded-Host: smartsoltech.kr
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 Если проблема осталась
|
||
|
||
### Диагностика 1: Проверить Django логи
|
||
|
||
```bash
|
||
# Смотреть логи в реальном времени
|
||
docker logs -f django_app
|
||
|
||
# Искать строки с CSRF:
|
||
# Forbidden (CSRF cookie not set.)
|
||
# Forbidden (CSRF token missing or incorrect)
|
||
# Origin checking failed - https://smartsoltech.kr does not match any trusted origins
|
||
```
|
||
|
||
### Диагностика 2: Проверить Nginx конфиг
|
||
|
||
```bash
|
||
# Проверить что изменения применились
|
||
sudo nginx -T | grep -A 10 "location /"
|
||
|
||
# Должны быть все proxy_set_header, особенно:
|
||
# proxy_set_header X-Forwarded-Host $host;
|
||
```
|
||
|
||
### Диагностика 3: Очистить кэш браузера
|
||
|
||
1. Открыть DevTools (F12)
|
||
2. Перейти в Network
|
||
3. Правый клик → "Clear browser cache"
|
||
4. Попробовать войти заново
|
||
|
||
### Диагностика 4: Временно включить DEBUG (ОСТОРОЖНО!)
|
||
|
||
```bash
|
||
# ТОЛЬКО ДЛЯ ДИАГНОСТИКИ! Выключить сразу после!
|
||
nano /opt/smartsoltech_site/.env
|
||
|
||
# Изменить
|
||
DEBUG=True
|
||
|
||
# Перезапустить
|
||
docker restart django_app
|
||
|
||
# Открыть админку - увидите детальную ошибку
|
||
|
||
# СРАЗУ ВЕРНУТЬ:
|
||
DEBUG=False
|
||
docker restart django_app
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 Объяснение что было исправлено
|
||
|
||
### Проблема в деталях:
|
||
|
||
1. **Браузер** → отправляет HTTPS запрос: `https://smartsoltech.kr/admin/login/`
|
||
2. **Nginx** → принимает HTTPS, проксирует к Django как HTTP: `http://localhost:8000`
|
||
3. **Django** → проверяет CSRF токен
|
||
4. **Django** → сравнивает Origin с CSRF_TRUSTED_ORIGINS
|
||
5. **❌ Ошибка:** В CSRF_TRUSTED_ORIGINS не было `https://smartsoltech.kr`
|
||
6. **❌ Ошибка:** Nginx не передавал `X-Forwarded-Host` → Django не знал реальный домен
|
||
7. **Результат:** Django отклонял запрос (403 Forbidden)
|
||
|
||
### Что исправили:
|
||
|
||
1. ✅ Добавили `https://smartsoltech.kr` в CSRF_TRUSTED_ORIGINS
|
||
2. ✅ Добавили `http://smartsoltech.kr` для HTTP→HTTPS redirect
|
||
3. ✅ Nginx передаёт `X-Forwarded-Proto: https`
|
||
4. ✅ Nginx передаёт `X-Forwarded-Host: smartsoltech.kr` (КРИТИЧНО!)
|
||
5. ✅ Django понимает что оригинальный запрос был HTTPS от smartsoltech.kr
|
||
6. ✅ CSRF проверка проходит успешно
|
||
|
||
---
|
||
|
||
## 🔐 Правильная финальная конфигурация
|
||
|
||
### 📄 .env (на сервере):
|
||
```env
|
||
DEBUG=False
|
||
ALLOWED_HOSTS=localhost,127.0.0.1,smartsoltech.kr,www.smartsoltech.kr,192.168.0.102
|
||
CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr,https://www.smartsoltech.kr
|
||
```
|
||
|
||
### 📄 Nginx /etc/nginx/sites-available/smartsoltech:
|
||
|
||
```nginx
|
||
upstream django_app {
|
||
server localhost:8000;
|
||
}
|
||
|
||
# HTTP → HTTPS redirect
|
||
server {
|
||
listen 80;
|
||
server_name smartsoltech.kr www.smartsoltech.kr;
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
# www → non-www redirect
|
||
server {
|
||
listen 443 ssl http2;
|
||
server_name www.smartsoltech.kr;
|
||
|
||
ssl_certificate /etc/letsencrypt/live/www.smartsoltech.kr/fullchain.pem;
|
||
ssl_certificate_key /etc/letsencrypt/live/www.smartsoltech.kr/privkey.pem;
|
||
|
||
return 301 https://smartsoltech.kr$request_uri;
|
||
}
|
||
|
||
# Main HTTPS server
|
||
server {
|
||
listen 443 ssl http2;
|
||
server_name smartsoltech.kr;
|
||
|
||
# SSL certificates (ваши существующие сертификаты)
|
||
ssl_certificate /etc/letsencrypt/live/www.smartsoltech.kr/fullchain.pem;
|
||
ssl_certificate_key /etc/letsencrypt/live/www.smartsoltech.kr/privkey.pem;
|
||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||
|
||
# Security headers
|
||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||
add_header X-Content-Type-Options "nosniff" always;
|
||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||
|
||
# Static files (ПЕРЕД location /)
|
||
location /static/ {
|
||
alias /opt/smartsoltech_site/smartsoltech/staticfiles/;
|
||
expires 30d;
|
||
add_header Cache-Control "public, immutable";
|
||
}
|
||
|
||
location /media/ {
|
||
alias /opt/smartsoltech_site/smartsoltech/media/;
|
||
expires 7d;
|
||
}
|
||
|
||
# Django proxy (с CSRF заголовками)
|
||
location / {
|
||
proxy_pass http://django_app;
|
||
|
||
# КРИТИЧНО для CSRF:
|
||
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;
|
||
proxy_set_header X-Forwarded-Host $host; # ← ВАЖНО!
|
||
|
||
proxy_redirect off;
|
||
proxy_buffering off;
|
||
proxy_read_timeout 60s;
|
||
proxy_connect_timeout 60s;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## ⚡ Быстрое решение одной командой
|
||
|
||
```bash
|
||
# НА СЕРВЕРЕ (выполнить всё за раз):
|
||
cd /opt/smartsoltech_site && \
|
||
git pull origin master && \
|
||
sudo cp nginx-smartsoltech-fixed.conf /etc/nginx/sites-available/smartsoltech && \
|
||
sudo nginx -t && sudo systemctl reload nginx && \
|
||
sed -i 's|CSRF_TRUSTED_ORIGINS=.*|CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr,https://www.smartsoltech.kr|' .env && \
|
||
docker restart django_app && \
|
||
sleep 3 && \
|
||
echo "✅ CSRF исправлен! Проверьте: https://smartsoltech.kr/admin/" && \
|
||
docker ps | grep django_app
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ Финальный чеклист
|
||
|
||
- [ ] Git pull выполнен (получен nginx-smartsoltech-fixed.conf)
|
||
- [ ] Nginx конфиг скопирован в /etc/nginx/sites-available/smartsoltech
|
||
- [ ] `sudo nginx -t` успешно (OK)
|
||
- [ ] `sudo systemctl reload nginx` выполнен
|
||
- [ ] CSRF_TRUSTED_ORIGINS обновлён в .env (добавлены http:// и https:// версии)
|
||
- [ ] `docker restart django_app` выполнен
|
||
- [ ] Контейнер django_app запущен (docker ps)
|
||
- [ ] **Вход в админку работает без ошибки 403** ✅
|
||
- [ ] DEBUG=False в .env (безопасность)
|
||
|
||
---
|
||
|
||
## 📞 Если нужна помощь
|
||
|
||
Если после всех шагов проблема осталась, отправьте:
|
||
|
||
```bash
|
||
# 1. Django логи
|
||
docker logs django_app --tail 100
|
||
|
||
# 2. Nginx конфиг
|
||
sudo nginx -T | grep -A 30 "server_name smartsoltech.kr"
|
||
|
||
# 3. Переменные окружения Django
|
||
docker exec django_app env | grep -E "(CSRF|ALLOWED|DEBUG)"
|
||
|
||
# 4. Тест curl
|
||
curl -v https://smartsoltech.kr/admin/ 2>&1 | head -50
|
||
```
|
||
|
||
---
|
||
|
||
**Создано:** 24 ноября 2025 г.
|
||
**Проблема:** CSRF 403 ошибка при входе в админку через Nginx + SSL
|
||
**Решение:** Добавить домен в CSRF_TRUSTED_ORIGINS + исправить Nginx заголовки (X-Forwarded-Host)
|
||
**Статус:** ✅ Готово к применению
|
||
|
||
|
||
# НЕ должно быть:
|
||
# HTTP/1.1 403 Forbidden
|
||
```
|
||
|
||
### Проверка 2: Через браузер
|
||
|
||
1. Открыть http://smartsoltech.kr/admin/
|
||
2. Ввести логин и пароль
|
||
3. Нажать "Войти"
|
||
4. ✅ Должно успешно войти в админку
|
||
5. ❌ НЕ должно показывать ошибку CSRF 403
|
||
|
||
### Проверка 3: Логи Django
|
||
|
||
```bash
|
||
docker logs django_app --tail 20 | grep -i csrf
|
||
|
||
# НЕ должно быть:
|
||
# "CSRF verification failed"
|
||
# "Forbidden (CSRF token missing or incorrect)"
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 Понимание настроек CSRF
|
||
|
||
### CSRF_TRUSTED_ORIGINS - что это?
|
||
|
||
Django сравнивает HTTP заголовок `Origin` с этим списком:
|
||
|
||
```python
|
||
# В Django settings.py читается из .env:
|
||
CSRF_TRUSTED_ORIGINS = [
|
||
'http://localhost:8000', # Для локальной разработки
|
||
'http://smartsoltech.kr', # Для HTTP запросов (до получения SSL)
|
||
'https://smartsoltech.kr', # Для HTTPS запросов (после SSL)
|
||
]
|
||
```
|
||
|
||
### Когда нужен каждый origin:
|
||
|
||
| Origin | Когда используется |
|
||
|--------|-------------------|
|
||
| `http://localhost:8000` | Django напрямую (без Nginx) |
|
||
| `http://smartsoltech.kr` | Через Nginx БЕЗ SSL |
|
||
| `https://smartsoltech.kr` | Через Nginx С SSL |
|
||
|
||
### После получения SSL сертификата:
|
||
|
||
```env
|
||
# Можно убрать HTTP, оставить только HTTPS:
|
||
CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr,https://www.smartsoltech.kr
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 Дополнительные проверки
|
||
|
||
### Если ошибка всё ещё есть:
|
||
|
||
#### 1. Проверить что Django видит правильные заголовки
|
||
|
||
```bash
|
||
# Включить DEBUG=True временно
|
||
nano /opt/smartsoltech_site/.env
|
||
# DEBUG=True
|
||
|
||
docker compose restart django_app
|
||
|
||
# Открыть http://smartsoltech.kr/admin/ в браузере
|
||
# Django покажет детальную информацию об ошибке
|
||
```
|
||
|
||
#### 2. Проверить ALLOWED_HOSTS
|
||
|
||
```bash
|
||
# В .env должно быть:
|
||
ALLOWED_HOSTS=localhost,127.0.0.1,smartsoltech.kr,www.smartsoltech.kr
|
||
```
|
||
|
||
#### 3. Проверить что Nginx передаёт правильный Host
|
||
|
||
```bash
|
||
# В логах Django должен быть правильный host
|
||
docker logs django_app --tail 50 | grep "GET /admin"
|
||
|
||
# Должно показывать:
|
||
# [INFO] "GET /admin/ HTTP/1.1" 200
|
||
# Host: smartsoltech.kr
|
||
```
|
||
|
||
#### 4. Тест с curl
|
||
|
||
```bash
|
||
# Симуляция браузера с Origin заголовком
|
||
curl -H "Origin: http://smartsoltech.kr" \
|
||
-H "Referer: http://smartsoltech.kr/admin/" \
|
||
-I http://smartsoltech.kr/admin/login/
|
||
|
||
# Должно вернуть 200 или 302, НЕ 403
|
||
```
|
||
|
||
---
|
||
|
||
## ⚠️ Важные замечания
|
||
|
||
### Безопасность
|
||
|
||
1. **DEBUG=False в продакшене** - всегда!
|
||
```env
|
||
DEBUG=False # ✅ В продакшене
|
||
DEBUG=True # ❌ Только для отладки локально
|
||
```
|
||
|
||
2. **HTTPS рекомендуется** - после получения SSL:
|
||
```nginx
|
||
# Редирект всех HTTP на HTTPS
|
||
server {
|
||
listen 80;
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
```
|
||
|
||
3. **www.smartsoltech.kr** - если используется:
|
||
```env
|
||
ALLOWED_HOSTS=localhost,127.0.0.1,smartsoltech.kr,www.smartsoltech.kr
|
||
CSRF_TRUSTED_ORIGINS=http://smartsoltech.kr,http://www.smartsoltech.kr,https://smartsoltech.kr,https://www.smartsoltech.kr
|
||
```
|
||
|
||
### После получения SSL
|
||
|
||
1. Обновить Nginx конфиг (раскомментировать SSL строки)
|
||
2. Обновить .env:
|
||
```env
|
||
# Можно убрать HTTP origins (оставить только для localhost)
|
||
CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr,https://www.smartsoltech.kr
|
||
```
|
||
3. Перезапустить:
|
||
```bash
|
||
sudo systemctl reload nginx
|
||
docker compose restart django_app
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 Быстрое решение (одной командой)
|
||
|
||
```bash
|
||
# На сервере выполнить всё сразу:
|
||
cd /opt/smartsoltech_site && \
|
||
git pull origin master && \
|
||
sudo cp nginx-smartsoltech.conf /etc/nginx/sites-available/smartsoltech && \
|
||
sudo nginx -t && \
|
||
sudo systemctl reload nginx && \
|
||
sed -i 's|CSRF_TRUSTED_ORIGINS=.*|CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr|' .env && \
|
||
docker compose restart django_app && \
|
||
echo "✅ CSRF исправлен! Проверьте: http://smartsoltech.kr/admin/"
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 Чеклист исправления
|
||
|
||
- [ ] Nginx конфиг обновлён (добавлены X-Forwarded-Host и X-Forwarded-Port)
|
||
- [ ] `sudo nginx -t` успешно
|
||
- [ ] `sudo systemctl reload nginx` выполнен
|
||
- [ ] `.env` обновлён (добавлен http://smartsoltech.kr в CSRF_TRUSTED_ORIGINS)
|
||
- [ ] Django перезапущен (`docker compose restart django_app`)
|
||
- [ ] Админка открывается без ошибки 403
|
||
- [ ] Вход в админку работает
|
||
- [ ] Логи Django без ошибок CSRF
|
||
|
||
---
|
||
|
||
**Создано:** 24 ноября 2025 г.
|
||
**Проблема:** CSRF 403 при входе в админку через Nginx
|
||
**Решение:** Добавить X-Forwarded заголовки в Nginx + добавить http://smartsoltech.kr в CSRF_TRUSTED_ORIGINS
|