Почему запросы к API падают из-за CORS и как это ломает продукт
Ошибка CORS почти никогда не приходит как понятная диагностика — браузер просто блокирует ответ, а в консоли остаётся сухая строка про отсутствующий заголовок. Для продукта это выглядит иначе: форма входа «крутится» без ответа, корзина не подтягивает остатки, интеграция с внешним сервисом молча возвращает пустоту. Пользователь считает, что сервис сломан, хотя бэкенд отработал корректно и вернул данные — их отбросил браузер на этапе проверки политики источника.
Второй типовой сбой — отклонённый предварительный запрос. Браузер перед «сложным» запросом к API отдельно спрашивает у сервера разрешение, и если в ответе нет нужных заголовков, до самого вызова дело не доходит. Отсюда классическая картина: в тестовом окружении всё работает, а на проде запросы к API падают, потому что политика источника настроена под конкретный домен и не учитывает боевой адрес или поддомен. Расхождение окружений по настройке CORS — самая дорогая категория таких сбоев: она не воспроизводится локально и всплывает уже у клиента.
Цена вопроса не в самой ошибке, а в том, как её ищут. Пока команда выясняет, дело в сервере, прокси или фронтенде, релиз стоит, а часть пользователей уходит. Разбираем, как настройка CORS закрывается на уровне конфигурации, чтобы такие отказы не доходили до продакшена.
Что даёт корректная настройка CORS: эффект в цифрах
Корректная настройка CORS убирает целый класс отказов, который иначе всплывает уже в проде: браузер блокирует запрос, фронтенд получает пустой ответ вместо данных, а в логах бэкенда при этом чисто — запрос до сервера просто не доходит. Диагностика такого инцидента занимает часы, потому что симптом виден только в консоли браузера у конкретного пользователя, а не в мониторинге API. Когда политика описана явно — списком разрешённых источников, методов и заголовков, — этот сценарий перестаёт существовать как класс.
Время интеграции сокращается предсказуемо. Пока правила раздаются наугад — «разрешим всё звёздочкой, потом разберёмся», — каждая новая среда, поддомен или отдельный клиент превращается в отдельный раунд отладки: разработчик фронтенда и инженер бэкенда по очереди проверяют, чей заголовок не дошёл. Согласованная политика снимает эти итерации: новый источник добавляется одной правкой конфигурации, а не расследованием.
Стабильность API растёт за счёт того, что разрешения перестают быть всеобщими. Точечные правила не пускают на бэкенд посторонние источники, а предварительные запросы кэшируются браузером — повторные обращения к тем же ручкам идут без лишнего круга согласования. Это заметно на нагруженных экранах, где один рендер тянет десяток запросов.
Выпуск релизов ускоряется, потому что проверка политики становится частью обычного контура: правила лежат в репозитории рядом с кодом, проходят ревью и раскатываются вместе с приложением. Никто не правит их руками на боевом контуре в момент выкладки, а откат возвращает и код, и настройки разом. Именно на стыке этих эффектов — меньше инцидентов, меньше ручной работы, ровнее релизы — и видно, что настройка CORS это не формальность, а часть отказоустойчивости продукта.
Подходы к настройке CORS: сравнение вариантов
Настройка CORS — это всегда выбор точки, в которой приложение получает разрешающие заголовки. Их можно отдавать на самом сервере API, подставлять на промежуточном слое или снять вопрос архитектурно, чтобы браузер вообще не видел кросс-доменный запрос. Ниже — четыре рабочих варианта, между которыми реально выбирают на проектах, с честными ограничениями каждого.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Разрешающие заголовки на сервере API | Свой бэкенд, ограниченный список фронтенд-доменов, нужны cookie и авторизация | Список доменов надо поддерживать вручную; при нескольких окружениях легко получить рассинхрон; ошибку в заголовках видно только в браузере |
| Проксирование через свой домен | Сторонний API без доступа к его настройкам, быстрый обход ограничений браузера | Прокси становится точкой отказа и требует ресурсов; растёт задержка; нужно отдельно решать авторизацию и лимиты |
| API-шлюз перед сервисами | Несколько сервисов, единые правила доступа, нужна централизованная политика | Требует инфраструктуры и сопровождения; для одного сервиса избыточен; правила шлюза надо тестировать отдельно от кода |
| Ручное управление заголовками в коде | Прототип, внутренний инструмент, разовый запрос | Дублирование логики в каждом сервисе; при рефакторинге правила теряются; не масштабируется на команду |
Разрешающие заголовки на сервере — базовый вариант: он даёт точный контроль над тем, каким доменам и с какими методами разрешены запросы, и корректно работает с сессионными cookie. Проксирование выручает, когда чужой API нельзя трогать: запрос идёт на ваш домен, а сервер уже пересылает его дальше — браузер видит обычный запрос без кросс-доменной проверки.
API-шлюз имеет смысл, когда сервисов больше одного: правила доступа описываются один раз и применяются ко всем, а не расползаются по конфигурациям. Ручная правка заголовков годится только как временное решение — она не переживает рост команды и первый же рефакторинг.
На практике выбор зависит от того, кому принадлежит API и сколько окружений нужно поддерживать. Разберём вашу схему запросов — вернёмся с оценкой, какой вариант закроет задачу без лишнего слоя.
Этапы внедрения: от аудита запросов до передачи регламента
Настройка CORS — это не одна правка в конфиге сервера, а последовательность шагов, где каждый следующий опирается на предыдущий. Если пропустить аудит и сразу выставить заголовки, часть запросов отвалится, а часть останется открытой шире, чем нужно. Поэтому ведём работу по этапам с проверкой на каждом.
- Аудит запросов. Собираем все источники, с которых фронтенд обращается к API: домены, поддомены, локальные адреса разработчиков, сторонние виджеты. Фиксируем методы, заголовки и сценарии с учётными данными — именно они чаще всего ломаются при неверной политике.
- Проектирование политики. Определяем, какие источники допускаем явно, какие методы и заголовки разрешаем, нужны ли учётные данные и как обрабатываем предварительные запросы. Вместо шаблона «разрешить всем» получаем список правил под реальные сценарии.
- Настройка. Переносим правила на сервер и в промежуточный слой: заголовки ответа, обработка предварительных запросов, кэширование разрешений. Отдельно проверяем, что политика не конфликтует с прокси и балансировщиком.
- Проверка сценариев. Прогоняем реальные цепочки: вход, загрузка файлов, запросы с учётными данными, обращения со сторонних доменов. Смотрим, что разрешённое проходит, а запрещённое отклоняется с понятной ошибкой.
- Документирование. Описываем действующие правила, причины каждого разрешения и порядок добавления нового источника — чтобы следующая правка не превращалась в археологию.
- Передача. Отдаём регламент команде, фиксируем ответственных и точки контроля. Политика становится частью процесса, а не разовым патчем.
Такой порядок убирает класс ошибок, когда браузер блокирует запрос, а причина неочевидна. Опишите задачу — вернёмся с оценкой и планом по этапам.
Что заказчик получает на руки: конфигурации и регламенты
Проект закрывается не презентацией, а набором артефактов, по которым другой инженер сможет поднять окружение и повторить настройку без нас. Ниже — что именно передаём по итогам работ над веб-приложением и его взаимодействием с внешними сервисами.
- Файлы конфигурации — рабочие конфиги для каждого окружения (разработка, тест, продуктив) с явным списком разрешённых источников, методов и заголовков. Отдельно фиксируем, какие значения задаются переменными окружения, а какие зашиты осознанно.
- Схемы взаимодействия — диаграммы потоков запросов между фронтендом, бэкендом и внешними API: кто инициатор, где проходит предварительный запрос, на каком узле срабатывает политика доступа.
- Политики доступа — правила, по которым домен попадает в белый список, порядок добавления нового источника и требования к его проверке. Это снимает ситуацию, когда правку вносит первый, кто добрался до конфига.
- Инструкции по развёртыванию и откату — пошаговый порядок применения изменений с точками возврата: если новая политика ломает интеграцию, откат занимает минуты, а не часы поиска причины.
- Чек-листы проверки — набор сценариев, которые прогоняются перед выкладкой: запрос с разрешённого домена, с чужого, предварительный запрос, запрос с учётными данными. Каждый пункт — с ожидаемым результатом.
- Регламент реагирования — что делать, когда в браузере пользователя появляется ошибка доступа: какие данные собрать, где смотреть журналы, кому эскалировать.
- Журнал изменений политик — кто, когда и зачем расширил список источников. Через полгода это единственный способ понять, почему открыт доступ тому или иному домену.
Комплект рассчитан на то, что команда заказчика ведёт проект сама: конфигурации читаемы, схемы отражают реальное состояние, чек-листы воспроизводимы. Опишите вашу задачу — вернёмся с оценкой и планом, в каком объёме эти артефакты закрывают ваш контур.
Кейс: как мы устранили сбои запросов и ускорили выпуск
Клиент — сервис с личным кабинетом и мобильным приложением — пришёл с симптомом, который команда не могла поймать месяцами: часть запросов к API падала с ошибкой политики безопасности браузера, но только у половины пользователей и только в проде. На стенде всё работало, у разработчиков — тоже. Воспроизводимость нулевая, а жалобы в поддержку росли.
Что нашли
Причина оказалась составной. Обратный прокси отдавал разные заголовки для запросов с Origin и без него, а предварительный запрос OPTIONS на части маршрутов уходил в приложение вместо прокси и возвращал редирект — браузер такой ответ не принимает и блокирует основной запрос. Плюс список разрешённых источников был захардкожен в коде и не покрывал новый поддомен, который добавили под акции. По отдельности каждый фактор безобиден, вместе — плавающие сбои.
Что изменили
Мы вынесли политику в конфигурацию прокси: единый список источников, явная обработка OPTIONS до попадания в приложение, корректный набор Access-Control-Allow-Methods и Access-Control-Allow-Headers под реально используемые методы и заголовки. Для запросов с учётными данными зафиксировали конкретные источники вместо шаблона — браузер не принимает широкий шаблон вместе с передачей сессионных данных. Отдельно закрыли кэширование предварительных ответов, чтобы правки политики применялись без ожидания.
Метрики до и после
| Показатель | До | После |
|---|---|---|
| Доля запросов с ошибкой политики | 6-9% в часы пик | 0 |
| Обращения в поддержку по «не работает вход» | 40+ в неделю | единичные |
| Время на добавление нового поддомена | 2-3 дня правок и релиз | правка конфигурации за минуты |
| Выпуск изменений в API | с опаской, точечно | штатный цикл |
Отдельный эффект — предсказуемость выпуска. Раньше любая правка доменов или заголовков тянула за собой релиз приложения и ручную проверку, теперь это изменение конфигурации, которое проходит ревью и откатывается одной строкой. Команда перестала бояться трогать интеграции.
Мы работали в связке с их Cloudflare и внутренним прокси, не меняя архитектуру приложения: правки легли на уровень, где им и место. Если у вас похожая картина — сбои «то есть, то нет» и долгий путь любой правки до прода — опишите задачу, вернёмся с оценкой и планом.
Чем отличается настройка CORS на сервере и на шлюзе?
На сервере политику задаёт само приложение: заголовки Access-Control-Allow-Origin, Allow-Methods, Allow-Headers формируются в обработчике или middleware и уходят в каждом ответе. Это даёт максимальную гибкость — правила можно строить на данных запроса: пускать конкретный источник только для определённых маршрутов, подставлять разные наборы методов для чтения и записи, вести журнал отказов рядом с бизнес-логикой. Ограничение одно: политика живёт внутри кода, и при нескольких сервисах её приходится описывать в каждом, рискуя получить расхождения между ними.
Шлюз выносит ту же политику на уровень перед приложениями. Правила описываются декларативно и применяются ко всем маршрутам сразу, единообразно, независимо от того, на чём написан сервис. Предварительный запрос OPTIONS обрабатывается на входе и до приложения не доходит — бэкенд не тратит ресурсы на служебные вызовы. Обратная сторона: шлюз видит только внешний контур запроса и не знает внутренней логики, поэтому тонкие правила вроде «источник A — только для чтения, источник B — только для записи» на нём выражаются грубее.
Ключевое различие — в точке применения политики. На сервере она часть приложения и меняется вместе с релизом; на шлюзе это конфигурация инфраструктуры, которую правят отдельно от кода и раскатывают централизованно. Отсюда и разная цена ошибки: неверный заголовок в коде чинится новым деплоем сервиса, неверное правило на шлюзе — правкой конфигурации без пересборки приложений.
Выбор зависит от контура. Один сервис с несколькими источниками — серверный вариант проще и точнее. Несколько сервисов за общим входом — шлюз убирает дублирование и держит единый стандарт. Часто применяют оба уровня: шлюз закрывает общий периметр и служебные запросы, а приложение донастраивает правила под свои маршруты.
Частые технические сомнения при настройке CORS
Прежде чем менять заголовки ответов, стоит понять: CORS — это не «настройка на сервере», а согласованный контракт между источником запроса и API. Большинство сомнений возникает там, где фронтенд и бэкенд правят свои части независимо и проверяют результат только в браузере — по факту появления ошибки в консоли.
Решение уже развёрнуто, но запросы всё равно падают — что проверять первым?
Смотреть на предварительный запрос: если он уходит и возвращается без нужных заголовков, основной запрос до API вообще не доходит. Дальше — порядок обработки: политика на уровне обратного прокси может перезаписывать то, что выставляет приложение. И третий частый источник — кэш: браузер держит результат предварительного запроса, поэтому правки не видны до сброса. Проверяем цепочку целиком, а не только код приложения.
Как избежать конфликтов между прокси, приложением и внешним сервисом?
Держать один источник истины: список разрешённых источников задаётся в одном месте, остальные слои его только пробрасывают, не дописывая свои значения. Если API отдаёт данные сторонним сайтам, политика описывается отдельно и явно — иначе первый же добавленный домен ломает остальных. Конфликты почти всегда следят за дублированием правил, а не за их содержанием.
Как проверять, что настройка действительно работает?
Автотест на предварительный запрос с реальными источниками и методами — он ловит регресс раньше, чем пользователь. Плюс проверка из браузера с чистым профилем и из командной строки: они показывают разные слои. Отдельно фиксируем поведение при отказах — какой ответ получает клиент, когда источник не разрешён.
Что делать с уже работающими интеграциями при изменении политики?
Вводить изменения поэтапно: сначала расширяем список, наблюдаем за обращениями, только потом убираем лишнее. Логи запросов с отклонёнными источниками дают точную картину, кто ещё ходит к API. Так перестройка проходит без внезапных отказов у смежных команд.
Если по вашей ситуации остались вопросы — опишите текущую схему: где стоит API, кто к нему обращается и как сейчас устроена проверка. Вернёмся с разбором и планом правок.
Обсудим вашу задачу и вернёмся с оценкой и планом
Чтобы вернуться с оценкой и планом, а не с общими словами, нам нужен контекст задачи. Настройка CORS почти никогда не сводится к одному заголовку в ответе сервера — важно, откуда идут запросы, какие методы и заголовки реально используются, есть ли авторизация через cookie, сколько доменов и поддоменов участвует. Чем точнее вы опишете картину, тем меньше итераций уйдёт на согласование.
Опишите задачу в свободной форме — этого достаточно для старта:
- какие домены и поддомены обращаются к вашему API (фронтенд, мобильное приложение, партнёрские интеграции);
- какие методы нужны: только чтение или ещё запись, удаление, предварительные запросы;
- передаются ли учётные данные — cookie, токены в заголовках, сессии;
- где сейчас возникает ошибка: в браузере, в логах сервера, только у части пользователей;
- какой стек на фронтенде и бэкенде, кто отвечает за конфигурацию сервера и обратного прокси;
- есть ли ограничения: требования безопасности, регламенты, запрет на правки в чужом сервисе.
Мы изучим контекст, воспроизведём поведение на вашей схеме запросов и предложим решение — с планом работ, сроками и тем, что нужно с вашей стороны. Если задача решается точечной правкой политики, скажем прямо; если нужна переработка схемы доступа, тоже обозначим честно, без навязывания лишнего объёма.
Обсудим задачу — напишите в удобном канале, приложите примеры запросов и ответов, если они есть. Вернёмся с оценкой и планом.







