С чего начать: алгоритм экстренной диагностики ошибок 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.logNginx: IP-адреса клиентов, коды ответа upstream (например,499,502), URI запросов. - В
error.logNginx: сообщения вида"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_passNginx приведет к ошибкам соединения, которые бэкенд может интерпретировать как отказ в доступе. - 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.
Окончательная проверка цепочки авторизации
Выполните итоговый чек-лист:
- Заголовок доходит до бэкенда: Проверьте логи бэкенд-приложения. Должна появиться строка, показывающая полученный заголовок
Authorization. - Токен валидируется: В логах бэкенда ищите сообщение об успешной аутентификации пользователя (например,
"User authenticated: id=123"). - Клиент получает корректный ответ: Запрос
curlили запрос из браузера возвращает ожидаемые данные (код 200) или корректную ошибку бизнес-логики (не 401/403). - 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, но и предотвратить их появление в будущем.