Files
smartsoltech_site/CSRF_FIX.md
Andrew K. Choi ea677183ca Fix CSRF 403 error: add X-Forwarded-Host header to Nginx and update CSRF_TRUSTED_ORIGINS
- 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
2025-11-24 12:03:26 +09:00

18 KiB
Raw Blame History

🔒 Исправление ошибки 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 конфигурацию

# На сервере
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

# Редактировать .env
cd /opt/smartsoltech_site
nano .env

НАЙТИ строку:

CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr

ЗАМЕНИТЬ НА:

CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr,https://www.smartsoltech.kr

Сохранить: Ctrl+O, Enter, Ctrl+X

Перезапустить Django:

docker restart django_app

# Проверить что контейнер запущен
docker ps | grep django_app

Вариант 2: Ручное исправление

Шаг 1: Исправить Nginx конфиг

sudo nano /etc/nginx/sites-available/smartsoltech

Найти секцию location / и добавить заголовки:

    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;
        ...
    }

Проверить и перезагрузить:

sudo nginx -t
sudo systemctl reload nginx

Шаг 2: Исправить .env файл

cd /opt/smartsoltech_site
nano .env

Найти строку:

CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr

Заменить на:

CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://smartsoltech.kr,https://smartsoltech.kr

Сохранить: Ctrl+O, Enter, Ctrl+X

Шаг 3: Перезапустить Django

docker compose restart django_app

# Проверить логи
docker logs django_app --tail 50

🧪 Проверка работы

Тест 1: Проверка переменных окружения

# Убедиться что 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 заголовков

# Проверить что Nginx передаёт все нужные заголовки
curl -v https://smartsoltech.kr/admin/ 2>&1 | grep -i forward

# Должны быть:
# X-Forwarded-Proto: https
# X-Forwarded-Host: smartsoltech.kr

🔧 Если проблема осталась

Диагностика 1: Проверить Django логи

# Смотреть логи в реальном времени
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 конфиг

# Проверить что изменения применились
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 (ОСТОРОЖНО!)

# ТОЛЬКО ДЛЯ ДИАГНОСТИКИ! Выключить сразу после!
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 (на сервере):

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:

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;
    }
}

Быстрое решение одной командой

# НА СЕРВЕРЕ (выполнить всё за раз):
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 (безопасность)

📞 Если нужна помощь

Если после всех шагов проблема осталась, отправьте:

# 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 с этим списком:

# В 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 сертификата:

# Можно убрать HTTP, оставить только HTTPS:
CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr,https://www.smartsoltech.kr

🔧 Дополнительные проверки

Если ошибка всё ещё есть:

1. Проверить что Django видит правильные заголовки

# Включить DEBUG=True временно
nano /opt/smartsoltech_site/.env
# DEBUG=True

docker compose restart django_app

# Открыть http://smartsoltech.kr/admin/ в браузере
# Django покажет детальную информацию об ошибке

2. Проверить ALLOWED_HOSTS

# В .env должно быть:
ALLOWED_HOSTS=localhost,127.0.0.1,smartsoltech.kr,www.smartsoltech.kr

3. Проверить что Nginx передаёт правильный Host

# В логах Django должен быть правильный host
docker logs django_app --tail 50 | grep "GET /admin"

# Должно показывать:
# [INFO] "GET /admin/ HTTP/1.1" 200
# Host: smartsoltech.kr

4. Тест с curl

# Симуляция браузера с 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 в продакшене - всегда!

    DEBUG=False  # ✅ В продакшене
    DEBUG=True   # ❌ Только для отладки локально
    
  2. HTTPS рекомендуется - после получения SSL:

    # Редирект всех HTTP на HTTPS
    server {
        listen 80;
        return 301 https://$host$request_uri;
    }
    
  3. www.smartsoltech.kr - если используется:

    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:
    # Можно убрать HTTP origins (оставить только для localhost)
    CSRF_TRUSTED_ORIGINS=http://localhost:8000,https://smartsoltech.kr,https://www.smartsoltech.kr
    
  3. Перезапустить:
    sudo systemctl reload nginx
    docker compose restart django_app
    

🎯 Быстрое решение (одной командой)

# На сервере выполнить всё сразу:
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