Skip to Content
БраузерСеть, CORS и кэш

Сеть, CORS и кэш

Уровень: middle.

Вопрос

Почему fetch на другой домен падает в консоли, хотя сервер ответил 200?

Короткий ответ

Браузер сам ходит на другой источник. CORS решает, можно ли странице прочитать ответ скриптом. Запрос уходит, сервер может ответить 200, а JS получит ошибку, если в ответе нет Access-Control-Allow-Origin, разрешающего этот origin.

Источник — схема, хост и порт. https://api.shop.com и https://shop.com — разные источники.

Простой запрос браузер шлёт сразу: GET, HEAD, POST с простыми заголовками и типом вроде text/plain или application/x-www-form-urlencoded. Запрос с Content-Type: application/json, своим заголовком или методом PUT сначала получает предзапрос OPTIONS. Сервер должен разрешить метод и заголовки. Предзапрос делает браузер, не ваш fetch.

Кэш HTTP: Cache-Control на ответе. no-store не сохраняет. max-age задаёт свежесть. ETag и Last-Modified позволяют условный запрос: 304, тело не качают заново. Это кэш браузера, не Pinia и не память вкладки.

Разбор

Cookie на кросс-доменный запрос уходит только с credentials: 'include' и ответом Access-Control-Allow-Credentials: true. При credentials заголовок Allow-Origin не может быть *, нужен конкретный origin.

Ошибка CORS не лечится на клиенте заголовком запроса. Заголовок ответа выставляет сервер. Прокси в dev-сервере Vite обходит это, потому что для браузера запрос на тот же origin, а сервер разработки ходит к API сам.

fetch не бросает на статусы 4xx и 5xx. Исключение — сеть, CORS, обрыв. Статус проверяют через response.ok или response.status.

На что смотрят на senior

Предзапрос кэшируется через Access-Control-Max-Age. Без этого каждый JSON POST платит лишний OPTIONS.

Приватный ответ с Authorization не должен получить случайный Cache-Control: public на CDN. Иначе один закэшированный JSON может уехать другому пользователю. Для таких ответов — private или no-store.

Service Worker — отдельный слой кэша поверх HTTP. На middle достаточно знать, что он перехватывает запросы и может отдать ответ офлайн. Стратегии cache-first и network-first выбирают по тому, что страшнее: устаревший бандл или пустой экран.

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

  • Искать CORS в Vue и добавлять заголовок на fetch.
  • Считать, что 200 в Network значит, что fetch успешно прочитан скриптом.
  • Забывать response.ok и парсить JSON ошибки как успешные данные.
  • Кэшировать на CDN ответ, который зависит от cookie сессии.

Задача

В Network у fetch('https://api.example.com/me') статус 200, в коде await fetch бросает исключение. Страница открыта с http://localhost:3000. Что проверить в ответе?

Показать ответ

Это разные источники: схема, хост и порт не совпадают. Ответ мог дойти, но браузер не отдаёт тело скрипту без Access-Control-Allow-Origin, который разрешает http://localhost:3000.

Смотреть заголовок ответа, не заголовок запроса. Если запрос с Content-Type: application/json, перед ним ещё будет OPTIONS. fetch при этом не бросает на HTTP 404: исключение здесь именно про CORS или сеть, не про статус 200.

Last updated on