🚧 CORS

💡 Основная идея

CORS — это механизм безопасности на стороне браузера, а не сервера — он решает, разрешено ли фронтенд JS читать кросс-доменный ответ, а не может ли сам запрос быть отправлен. Это невозможно исправить со стороны клиента, если сервер не отправляет нужные заголовки.


🌐 Origin

Origin — это тройка scheme + host + port. Два URL считаются одного происхождения только если совпадают все три компонента — http:// и https://, или :3000 и :8080 на одном и том же хосте, считаются разными origin.


🔀 Простые и preflight-запросы

  • Простые запросы (GET/HEAD/POST только с небольшим набором стандартных заголовков и типов контента) идут прямо на сервер. Ответ приходит обратно, и браузер решает постфактум, разрешено ли JS его читать, основываясь на Access-Control-Allow-Origin.
  • Preflight-запросы — всё, что использует не-простой метод (PUT, DELETE, PATCH) или кастомные заголовки (Authorization, Content-Type: application/json и т.д.) — сначала запускают OPTIONS-запрос, спрашивающий у сервера, что разрешено, до отправки реального запроса. Только если preflight-ответ сервера разрешает метод/заголовки, браузер отправляет фактический запрос.

⚙️ Настройка на стороне сервера

  • Явно разрешайте нужный Origin, методы и заголовки — не полагайтесь на значения по умолчанию.
  • Никогда не сочетайте Access-Control-Allow-Origin: * с Access-Control-Allow-Credentials: true — спецификация это запрещает, и браузеры отклоняют такое сочетание.
  • Для API-прокси (например, Nuxt/Node backend-for-frontend), маршрутизация запросов через собственный сервер полностью убирает кросс-доменный переход между браузером и бэкендом — заголовки CORS не нужны.
app.use(
  cors({
    origin: ["https://app.example.com"],
    credentials: true,
    allowedHeaders: ["content-type", "authorization"],
    methods: ["GET", "POST", "PATCH", "DELETE"],
  }),
)

🐞 Отладка

  • Ошибки CORS невозможно исправить на клиенте, если сервер не отправляет нужные заголовки — обходного пути на стороне клиента не существует (кроме использования прокси).
  • Проверьте вкладку Network на предмет ответа preflight-запроса OPTIONS и «blocked reason» браузера.
  • Если куки не отправляются кросс-доменно, проверьте Access-Control-Allow-Credentials на сервере и credentials: 'include' на клиенте — а также что Access-Control-Allow-Origin указывает точный origin, а не *.

HTTP & Network Cookies & Storage — SameSite и кросс-доменные куки browser