Диагностика и исправление ошибок 401 и 403 в Nginx, Docker и Kubernetes: практическое руководство | AdminWiki
Timeweb Cloud — сервера, Kubernetes, S3, Terraform. Лучшие цены IaaS.
Попробовать

Диагностика и исправление ошибок 401 и 403 в Nginx, Docker и Kubernetes: практическое руководство

19 апреля 2026 8 мин. чтения

С чего начать: алгоритм экстренной диагностики ошибок 401/403

Когда запросы начинают возвращать 401 или 403, а система аутентификации перестает работать, действовать нужно системно. Этот алгоритм позволяет за 10-15 минут локализовать проблему в цепочке Nginx → Docker/Kubernetes → бэкенд. Не меняйте конфигурации наугад. Сначала соберите данные.

Первые команды для анализа: куда смотреть и что искать

Начните с проверки логов всех компонентов. Используйте эти команды для быстрого получения последних записей.

# Логи Nginx на bare-metal или в контейнере
tail -f /var/log/nginx/error.log
tail -f /var/log/nginx/access.log | grep -E "401|403"

# Логи Docker-контейнера
docker logs --tail 50 -f <container_id_or_name>

# Логи пода в Kubernetes
kubectl logs -f <pod_name> --all-containers=true -n <namespace>
kubectl describe pod <pod_name> | grep -A5 Events

В логах ищите ключевые индикаторы:

  • В access.log Nginx: IP-адреса клиентов, коды ответа upstream (например, 499, 502), URI запросов.
  • В error.log Nginx: сообщения вида "upstream timed out", "no live upstreams", ошибки конфигурации.
  • В логах приложения (бэкенда): сообщения об отсутствии заголовка Authorization, ошибках валидации JWT, отказе в доступе по роли.
  • В событиях Kubernetes (kubectl describe pod): ошибки инициализации контейнера, проблемы с подачей секретов (Secrets) в переменные окружения.

Быстрая проверка цепочки запроса с помощью curl

Изолируйте компоненты, чтобы понять, где обрывается цепочка. Сравните прямой запрос к бэкенду и запрос через прокси.

# 1. Прямой запрос к бэкенду (минуя Nginx). Убедитесь, что сервис жив и токен валиден.
curl -v -H "Authorization: Bearer YOUR_JWT_TOKEN" http://backend-service:8080/api/protected

# 2. Запрос через Nginx (или Ingress). Сравните заголовки в выводе -v.
curl -v -H "Authorization: Bearer YOUR_JWT_TOKEN" https://your-domain.com/api/protected

# 3. Проверка ответа на CORS preflight-запрос (OPTIONS).
curl -v -X OPTIONS https://your-domain.com/api/protected \
  -H "Origin: http://your-frontend-app.com" \
  -H "Access-Control-Request-Method: POST"

Анализируйте вывод curl -v. Убедитесь, что заголовок Authorization присутствует в запросе (> Authorization: Bearer ...). Проверьте, что запрос уходит на правильный адрес upstream. Если на шаге 2 вы получаете 401, а на шаге 1 - 200, проблема в передаче заголовка через прокси.

Для комплексного понимания работы Nginx в production изучите полное руководство по структуре nginx.conf с готовыми примерами настроек секций main, events, http, server и location.

Главные причины 401/403: от прокси до политик кластера

Ошибки авторизации возникают на стыке компонентов. Понимание типовых точек отказа ускоряет диагностику.

Проблема 1: Заголовок авторизации «теряется» за Nginx

Самая частая причина 401 - Nginx по умолчанию не передает все заголовки HTTP-запроса бэкенду. Директива proxy_pass перенаправляет запрос, но без явного указания proxy_set_header заголовок Authorization (и другие, например, X-Forwarded-For) до бэкенда не дойдут.

Как проверить: найдите в логах бэкенда входящий HTTP-запрос. Если в логе приложения нет строки с заголовком Authorization, значит, Nginx его «съел». Бэкенд, не получив токен, закономерно возвращает 401 Unauthorized.

Проблема 2: CORS и preflight-запросы (OPTIONS)

Когда фронтенд на одном домене пытается вызвать API на другом, браузер отправляет предварительный запрос OPTIONS (preflight). Критичный нюанс: браузер не включает в этот запрос заголовки авторизации, такие как Authorization.

Если бэкенд или Nginx требуют авторизацию для всех методов, включая OPTIONS, запрос завершится с 401 или 403, и основной запрос (POST, GET) даже не будет отправлен. Проблема выглядит как внезапная ошибка авторизации в браузере, при этом curl может работать.

Проблема 3: Сетевая изоляция в Docker и Kubernetes

В контейнеризованных средах ошибки 403 Forbidden могут быть следствием сетевой изоляции, а не логики приложения.

  • Docker: Сервисы в разных пользовательских сетях Docker Compose по умолчанию не видят друг друга. Неправильно указанное имя хоста (алиас сервиса) в proxy_pass Nginx приведет к ошибкам соединения, которые бэкенд может интерпретировать как отказ в доступе.
  • Kubernetes NetworkPolicy: Это объект безопасности, который определяет, как группа подов может общаться друг с другом и с другими сетевыми конечными точками. Если для вашего приложения существует NetworkPolicy, разрешающий входящий трафик только с определенных подов (например, ingress-контроллера), а ваш сервис пытается получить доступ из другого неймспейса, трафик будет заблокирован на уровне сети. В логах приложения это может выглядеть как таймаут или отказ в соединении.

Для безопасного развертывания контейнеров в production ознакомьтесь с полным руководством по Docker в production, которое охватывает управление секретами, health checks и стратегии деплоя.

Готовые решения: конфигурации для Nginx, Docker Compose и Kubernetes

Используйте эти проверенные конфигурационные фрагменты. Адаптируйте их под свои адреса и имена сервисов.

Конфигурация Nginx: передача заголовков и настройка CORS

Этот блок location в конфигурации Nginx решает проблемы с передачей заголовков и корректно обрабатывает CORS-запросы.

server {
    listen 80;
    server_name api.your-domain.com;

    # Обработка preflight CORS-запросов (OPTIONS)
    location / {
        if ($request_method = OPTIONS) {
            add_header 'Access-Control-Allow-Origin' '*' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
            add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
            add_header 'Access-Control-Max-Age' 1728000 always;
            add_header 'Content-Type' 'text/plain; charset=utf-8' always;
            add_header 'Content-Length' 0 always;
            return 204;
        }

        # Передача оригинальных заголовков бэкенду
        proxy_set_header Authorization      $http_authorization;
        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;

        # Важно: указывайте полный URL upstream с протоколом
        proxy_pass http://backend-app:8080;

        # Настройки CORS для основных запросов
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
        add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
    }
}

Ключевой момент: директива proxy_set_header Authorization $http_authorization; явно передает исходный заголовок авторизации. Блок для OPTIONS возвращает успешный ответ 204 без проверки авторизации.

Docker Compose: настройка сетей и переменных окружения

Пример docker-compose.yml, где Nginx и бэкенд-приложение находятся в одной пользовательской сети и могут взаимодействовать по именам сервисов.

version: '3.8'

services:
  nginx-proxy:
    image: nginx:1.24-alpine
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./conf.d:/etc/nginx/conf.d:ro
    networks:
      - app-network
    depends_on:
      - backend-app

  backend-app:
    image: your-backend-app:latest
    container_name: backend-app
    expose:
      - "8080"
    environment:
      - JWT_SECRET_KEY=${JWT_SECRET}  # Секрет берется из .env файла хоста
      - DATABASE_URL=postgresql://db:5432/app
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

Создание явной сети app-network гарантирует, что контейнеры смогут разрешать имена сервисов (backend-app, db) друг друга. В Nginx конфиге upstream указывается как http://backend-app:8080.

Манифесты Kubernetes: Service, Ingress и NetworkPolicy

Базовые манифесты для развертывания приложения с учетом доступа.

Deployment и Service:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-app
spec:
  selector:
    matchLabels:
      app: backend-app
  template:
    metadata:
      labels:
        app: backend-app
    spec:
      containers:
      - name: app
        image: your-backend-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: JWT_SECRET_KEY
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: jwtSecret
---
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

Разрешающая NetworkPolicy: Если в кластере активирован сетевой плагин, поддерживающий NetworkPolicy (например, Calico), и по умолчанию трафик запрещен, создайте политику, разрешающую входящие запросы к подам приложения.

# networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-to-backend
spec:
  podSelector:
    matchLabels:
      app: backend-app
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          app: nginx-ingress-controller # Разрешаем трафик только от Ingress-контроллера
    ports:
    - protocol: TCP
      port: 8080

Для сложных сценариев балансировки нагрузки и high availability используйте продвинутые конфигурации Nginx как балансировщика нагрузки с health checks и graceful shutdown.

Верификация исправлений и безопасное внедрение

После внесения изменений убедитесь, что проблема решена полностью, а не частично. Затем спланируйте безопасное развертывание в production.

Окончательная проверка цепочки авторизации

Выполните итоговый чек-лист:

  1. Заголовок доходит до бэкенда: Проверьте логи бэкенд-приложения. Должна появиться строка, показывающая полученный заголовок Authorization.
  2. Токен валидируется: В логах бэкенда ищите сообщение об успешной аутентификации пользователя (например, "User authenticated: id=123").
  3. Клиент получает корректный ответ: Запрос curl или запрос из браузера возвращает ожидаемые данные (код 200) или корректную ошибку бизнес-логики (не 401/403).
  4. CORS работает: Ответы от API содержат заголовки Access-Control-Allow-*.

Стратегии для продакшена: canary-деплой и откат

В Kubernetes не обновляйте все поды сразу. Используйте стратегию canary-деплоя.

# 1. Создайте новый Deployment с обновленным конфигом (например, новой версией приложения или измененными env).
# 2. Настройте Ingress (например, Nginx Ingress Controller) так, чтобы небольшой процент трафика (10%) направлялся на новые поды.
# Пример аннотации для Nginx Ingress:
# annotations:
#   nginx.ingress.kubernetes.io/canary: "true"
#   nginx.ingress.kubernetes.io/canary-weight: "10"

# 3. Мониторьте логи и метрики новых подов. Если ошибок 401/403 нет, постепенно увеличивайте вес.

# 4. Для немедленного отката конфигурации Deployment используйте:
kubectl rollout undo deployment/backend-app

Этот подход минимизирует влияние возможной ошибки на всех пользователей. Для администрирования базовых компонентов инфраструктуры пригодится практическое руководство по администрированию Linux с готовыми командами и скриптами.

Приложение: справочник по версиям и частые ошибки

  • Актуальные версии (на апрель 2026): Конфигурации в статье проверены для Nginx 1.22+, Docker Engine 24.0+, Docker Compose v2.20+, Kubernetes 1.27+.
  • Наследование add_header в Nginx: Директива add_header в блоке location перезаписывает одноименные заголовки из родительских блоков (server, http). Если вам нужны заголовки из родительского блока, их необходимо продублировать. Это частая причина «исчезновения» заголовков HSTS или Security.
  • Ошибка в proxy_pass: Указание proxy_pass http://unix:/path/to/socket.sock; для контейнера в Docker без правильной привязки тома с сокетом приведет к ошибке 502 Bad Gateway.
  • Секреты в Kubernetes: Убедитесь, что Secret (app-secrets в примере) создан в том же неймспейсе, что и Deployment, и ключи в нем названы правильно.
  • Для углубленного изучения: Всегда сверяйтесь с официальной документацией - Nginx, Docker, Kubernetes. Это гарантирует актуальность информации для вашей конкретной версии.

Используя этот структурированный подход - от экстренной диагностики до безопасного внедрения готовых конфигураций - вы сможете не только быстро исправить ошибки 401 и 403, но и предотвратить их появление в будущем.

Поделиться:
Сохранить гайд? В закладки браузера