Что происходит при выполнении запроса с Postman с CORS?
Что проверяют
Понимаете ли вы, что CORS это правило браузера, а не сервера, и умеете ли по этому признаку отделять проблему фронта от проблемы бэкенда. Для питониста вопрос практический: в Postman ручка отвечает, из браузера падает, и надо сказать, где чинить.
Как отвечать
Postman не браузер: у него нет страницы с источником, нет политики одного источника, значит и проверять нечего. Запрос уходит как от обычного HTTP-клиента, сервер его выполняет, Postman показывает тело и заголовки, включая Access-Control-Allow-Origin, если он есть. На поведение Postman эти заголовки не влияют.
В браузере схема другая:
- простой запрос (GET, HEAD, POST с формой или текстом) уходит на сервер сразу, сервер его честно выполняет, и только ответ браузер прячет от скрипта, если
Access-Control-Allow-Originне совпал с источником страницы; - непростой (метод PUT, PATCH, DELETE, свой заголовок,
Content-Type: application/json) сначала порождает предварительныйOPTIONSсAccess-Control-Request-Method. Без корректного ответа на него основной запрос не отправится вовсе.
Отсюда вывод, который и хотят услышать: «в Postman работает, в браузере нет» почти всегда означает отсутствие заголовков CORS на сервере или необработанный OPTIONS. В FastAPI это CORSMiddleware, в Django django-cors-headers, во Flask flask-cors. Куки и Authorization требуют разрешения на передачу учётных данных, а вместе с ним звёздочка в списке источников запрещена, нужен конкретный домен.
Где спотыкаются
Говорят, что CORS защищает сервер от атак. Не защищает: простой запрос доходит и выполняется. CORS решает, отдаст ли браузер ответ чужому скрипту, а от подделки запросов защищают токены CSRF и атрибут SameSite у куки.
Второе: правят настройку CORS, когда проблема в другом слое. Если ошибка вернулась пятисоткой мимо middleware или роут описан только под POST и не отвечает на OPTIONS, заголовков в ответе не будет, а браузер пожалуется теми же словами.
Третье: проверив ручку в Postman, объявляют её рабочей. Postman не шлёт заголовок источника, не делает предварительный запрос и не хранит браузерные куки, поэтому браузерный сценарий в нём не воспроизвести.