Границы CORS в браузере: заголовки, preflight, учетные данные и кэш
Практическая диагностика cross-origin запросов без смешения CORS с сетью, авторизацией, CSP и политикой разрешений.
BotBrowser Team
Нужна структурированная документация по теме Платформа?
Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.
let pageOrigin = '';
let apiOrigin = '';
const observations = [];
const listen = server => new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
const close = server => new Promise(resolve => server.close(resolve));
const api = http.createServer((request, response) => {
const name = request.url.slice(1);
if (name === 'control') response.setHeader('Access-Control-Allow-Origin', pageOrigin);
observations.push({ name, status: 200, allowOrigin: response.getHeader('Access-Control-Allow-Origin') || null });
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end('{"fixture":true}');
});
const pageServer = http.createServer((request, response) => {
const name = request.url.slice(1);
response.end(<button>Run CORS check</button><output data-testid="cors-status"></output><script> document.querySelector('button').onclick = async () => { try { await fetch('${apiOrigin}/${name}'); document.querySelector('output').textContent = 'CORS_ALLOWED'; } catch { document.querySelector('output').textContent = 'FETCH_UNCLASSIFIED'; } };</script>);
});
await listen(api); await listen(pageServer);
const apiPort = api.address().port; const pagePort = pageServer.address().port;
pageOrigin = http://127.0.0.1:${pagePort};
apiOrigin = http://127.0.0.1:${apiPort};
const browser = await chromium.launch();
const context = await browser.newContext();
context.setDefaultNavigationTimeout(8_000);
async function runCase(name) {
const page = await context.newPage();
try {
await page.goto(${pageOrigin}/${name});
await page.getByRole('button', { name: 'Run CORS check' }).click();
await page.locator('[data-testid="cors-status"]').waitFor({ state: 'visible', timeout: 5_000 });
return await page.locator('[data-testid="cors-status"]').textContent();
} catch { return 'UNKNOWN'; }
finally { await page.close(); }
}
try {
const control = await runCase('control');
const candidate = await runCase('candidate');
assert.equal(control, 'CORS_ALLOWED');
assert.equal(candidate, 'FETCH_UNCLASSIFIED');
assert.deepEqual(observations.map(({ name, allowOrigin }) => ({ name, allowOrigin })), [
{ name: 'control', allowOrigin: pageOrigin }, { name: 'candidate', allowOrigin: null }
]);
} finally { await context.close(); await browser.close(); await close(pageServer); await close(api); }
Control должен показать `CORS_ALLOWED` после чтения синтетического тела. Candidate показывает `FETCH_UNCLASSIFIED` после отклонения Fetch; одного этого состояния недостаточно для CORS. Подтверждайте `CORS_BLOCKED`, только если лог собственного API показывает оба запроса и одинаковое тело со статусом 200, ответ candidate намеренно не содержит Allow-Origin, а control прочитал тот же endpoint с точным origin страницы.
Ошибка навигации, статуса или сервера остается `UNKNOWN`; ее нельзя приписывать сети или CORS. Навигация ограничена восемью секундами, каждая page закрывается при cleanup.
## Таблица диагностики
| Наблюдение | Первый владелец | Проверка | Осторожный вывод |
| ----------------------------------- | ---------------- | --------------------------------------------- | ----------------------------------------- |
| DNS, TLS или соединение не работают | Сеть | Маршрут, proxy, сервис | Ответ CORS не получен |
| Preflight 4xx/5xx | API | OPTIONS, auth, методы, заголовки | Предварительный обмен не прошел |
| 200, но скрипт не читает тело | API/браузер | Origin, режим учётных данных, exposed headers | CORS sharing не разрешен или ответ opaque |
| Cookie отсутствует | Auth/браузер | режим учётных данных, SameSite, Secure | CORS не делает cookie допустимой |
| Разные результаты для origin | API/кэш | Allow-list и `Vary: Origin` | Политика или кэш отличаются |
| Нарушение CSP | Веб-безопасность | `connect-src`, отчеты | Это блок CSP, не CORS |
Граница BotBrowser
Управляемые BrowserContext BotBrowser могут запускать авторизованные собственные control/candidate fixtures, изолировать синтетические cookies и хранилище и сравнивать видимые результаты Fetch на объявленной сборке браузера. Граница описана в [документации multi-account isolation](https://botbrowser.io/docs/identity/multi-account-isolation/).
Для проверки CORS BotBrowser может выполнить сравнение control/candidate, но не может настроить ответ API или обойти правило совместного использования браузера.
BotBrowser не пишет и не развертывает CORS-заголовки, не выдает серверную авторизацию, не исправляет сторонний API, не меняет origin и не обходит CORS браузера. Он также не доказывает завершение удаленной бизнес-операции. Сервер, CDN, учетные данные, CSP, политикой разрешений и состояние приложения принадлежат их владельцам.
Рабочий список
1. Опишите origin страницы и API, метод, заголовки, режим учётных данных и ожидаемый результат.
2. Проверьте DNS, TLS, proxy и маршрут до анализа policy-ошибки.
3. Запишите Origin, preflight, статус, CORS-заголовки, `Vary` и кэш.
4. Отделяйте CORS от CSP, политикой разрешений, пользовательского разрешения и авторизации.
5. Используйте собственный fixture с timeout навигации 8 секунд, статуса 5 секунд и cleanup.
6. Не смешивайте сетевую ошибку, timeout, ошибку preflight и CORS block.
**Автор:** BotBrowser Team / BotBrowser команда
См. [руководство по cross-origin isolation](/ru/blog/cross-origin-isolation-and-shared-memory-requirements/) и [руководство политикой разрешений](/ru/blog/permissions-policy-for-embedded-browser-features/).
## Sources
- Fixture URL (example only): https://app.example.test`
- Fixture URL (example only): https://app.example.test/control`
- [Протокол CORS Fetch](https://fetch.spec.whatwg.org/#http-cors-protocol)
- [CORS в MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
- [CSP в MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
- [политикой разрешений в MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy)
Похожие статьи
Переведите BotBrowser из исследований в продакшн
Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.