Как работает preflight запрос
Что проверяют
Понимаете ли вы, что CORS это ограничение браузера, а preflight отдельный запрос за разрешением, который сервер обязан уметь обработать. Вопрос обычно вырастает из бага «в Postman работает, в браузере нет».
Как отвечать
- Preflight это запрос
OPTIONSбез тела, который браузер шлёт сам перед непростым кросс-доменным запросом. - Простым считается запрос методом GET, HEAD или POST с безопасными заголовками и Content-Type из трёх значений:
application/x-www-form-urlencoded,multipart/form-data,text/plain.application/jsonв список не входит, поэтому JSON API получает preflight почти всегда. Свой заголовок, включая Authorization, тоже его вызывает. - В preflight уходят
Origin,Access-Control-Request-MethodиAccess-Control-Request-Headers. Куки и заголовки авторизации в нём не передаются. - Сервер отвечает кодом 2xx и заголовками
Access-Control-Allow-Origin,-Allow-Methods,-Allow-Headers, при необходимости-Allow-Credentials: trueи-Max-Age(кэш разрешения, Chrome режет его до 7200 секунд). Основной запрос уйдёт, только если разрешены и метод, и все заголовки. - С
credentials: "include"звёздочка запрещена: возвращают конкретный источник и добавляютVary: Origin, иначе кэш отдаст чужое разрешение.
OPTIONS /api/orders HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: PATCH
Access-Control-Request-Headers: authorization, content-typeГде спотыкаются
Считают, что CORS защищает сервер. Простой запрос сервер уже выполнил, браузер лишь прячет ответ от скрипта. Настоящая защита это авторизация и CSRF-токены.
Закрывают OPTIONS авторизацией и получают 401 или редирект на логин. Preflight идёт без кук, отвечать на него надо до проверки прав, а редиректы в нём запрещены.
Ставят Access-Control-Allow-Origin: * вместе с учётными данными: браузер такой ответ отклонит. Звёздочка в Allow-Headers не покрывает Authorization.
Отлаживают через curl и Postman, где нет ни источника, ни политики. Воспроизводить надо в браузере, глядя на OPTIONS во вкладке Network.