Skip to content

Как починить CORS preflight

Коротко:

CORS preflight — OPTIONS-запрос, который browser отправляет перед не-simple request (custom headers, non-GET). Если сервер не отвечает 200/204 с правильными CORS-headers, actual request не идёт. Частые fixes: ответить на OPTIONS в nginx, добавить Access-Control-Allow-Headers с вашими custom headers, установить Access-Control-Max-Age.

Ниже: пошаговая инструкция, рабочие примеры, типичные ошибки, FAQ.

Пошаговая настройка

  1. Определите что это preflight: DevTools → Network → OPTIONS перед actual запросом
  2. Проверьте response OPTIONS: status 200/204 + правильные CORS headers
  3. nginx: отдельный if ($request_method = OPTIONS) блок
  4. Ensure Access-Control-Allow-Headers содержит все ваши custom (X-Request-Id, Authorization)
  5. Max-Age кэширует preflight: Access-Control-Max-Age: 86400
  6. Express: app.options('*', cors()) для handle global preflight
  7. Verify: Enterno CORS checker → увидите preflight response

Рабочие примеры

СценарийКонфиг
nginx OPTIONS handlerlocation /api/ { if ($request_method = OPTIONS) { add_header 'Access-Control-Allow-Origin' '$http_origin'; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE'; add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Request-Id'; add_header 'Access-Control-Max-Age' 86400; return 204; } # ... actual proxy_pass }
Express.jsapp.use(cors({ origin: 'https://app.example.com', methods: ['GET','POST','PUT','DELETE'], allowedHeaders: ['Authorization','Content-Type','X-Request-Id'], credentials: true, maxAge: 86400 }));
Django settings.pyCORS_ALLOWED_ORIGINS = ['https://app.example.com'] CORS_ALLOW_HEADERS = ['authorization','content-type','x-request-id'] CORS_ALLOW_CREDENTIALS = True
Simple vs preflight triggersSimple: GET + только Accept+Content-Type (в whitelist) Preflight: PUT, PATCH, DELETE, или any custom header
Preflight caching (reduce load)Access-Control-Max-Age: 86400 # browser cache preflight 24h

Типичные ошибки

  • OPTIONS возвращает 405 (Method Not Allowed) — значит сервер не обрабатывает OPTIONS специально
  • Access-Control-Allow-Headers без вашего custom header — preflight fails
  • Use wildcard * в Origin при credentials=true — browser блокирует
  • Missing Access-Control-Max-Age → browser шлёт OPTIONS перед каждым request = 2x latency
  • CORS settings через PHP header() после session_start() may быть overridden proxy

Больше по теме

Часто задаваемые вопросы

Когда браузер делает preflight?

Non-simple request. Simple: GET/HEAD/POST + только standard headers. Любая custom header, PUT/DELETE/PATCH, Content-Type не в whitelist → preflight.

Preflight медленнее — как ускорить?

Access-Control-Max-Age: 86400 кэширует preflight 24 часа. Browser не повторяет OPTIONS на каждый request.

Django DRF + JWT = preflight требует Authorization?

Да. JWT в Authorization header → custom header → preflight обязателен. DRF-cors settings должен содержать "authorization".

Как тестировать?

<a href="/cors">Enterno CORS checker</a> → URL + Origin → увидите preflight ответ + headers.