Как сервер запрещает запросы с других доменов и как браузер понимает запрет?
Что проверяют
Понимаете ли вы, что запрещает не сервер, а браузер, и что сервер лишь сообщает, кому можно читать его ответы. Следом обычно спрашивают, что такое preflight и почему запрос из Postman проходит, а из браузера нет.
Как отвечать
В основе лежит Same-Origin Policy: скрипт со страницы одного источника (схема, домен и порт) не может читать ответы другого. CORS это способ для сервера ослабить правило заголовками:
- простой запрос:
GET,HEADилиPOSTсо стандартными заголовками иContent-Typeиз спискаtext/plain,multipart/form-data,application/x-www-form-urlencoded. Браузер отправляет его сразу с заголовкомOriginи смотрит наAccess-Control-Allow-Originв ответе; - preflight: для
PUT,DELETE, JSON-тела или заголовкаAuthorizationбраузер сначала шлётOPTIONSсAccess-Control-Request-MethodиAccess-Control-Request-Headers. Сервер отвечаетAccess-Control-Allow-MethodsиAccess-Control-Allow-Headers, аAccess-Control-Max-Ageкеширует разрешение; - куки: при
credentials: 'include'нуженAccess-Control-Allow-Credentials: true, аAccess-Control-Allow-Originне может быть*, только конкретный источник; - заголовки ответа: свои заголовки скрипт увидит, только если они перечислены в
Access-Control-Expose-Headers.
Как браузер понимает запрет: разрешающего заголовка нет или он не совпадает с Origin. Тогда ответ скрипту не отдаётся, fetch отклоняется с TypeError, статус и тело недоступны, подробности видны только в консоли. Если сервер подставляет источник динамически, ему нужен Vary: Origin, иначе кеш перепутает ответы.
Где спотыкаются
Думают, что CORS защищает сервер. Простой запрос доходит и выполняется, браузер лишь прячет ответ. От CSRF защищают SameSite и токены, а не CORS.
Лечат ошибку через mode: 'no-cors'. Запрос уйдёт, но ответ станет непрозрачным, и прочитать его нельзя.
Считают, что сервер должен вернуть 403. Браузеру важны заголовки, а не код: ответ 200 без Access-Control-Allow-Origin заблокирован точно так же.