# CORS

> Механизм браузера, позволяющий серверу явно разрешить страницам с других источников читать свои ответы — контролируемое ослабление политики одного источника.

Источник: https://llmdocs.ru/wiki/cors/ · обновлено 2026-11-09 · лицензия CC BY-SA 4.0

- **Полное название**: Cross-Origin Resource Sharing
- **Стандарт**: WHATWG Fetch Standard (ранее рекомендация W3C, 2014)
- **Основа**: политика одного источника (same-origin policy)
- **Источник (origin)**: схема + хост + порт
- **Ключевой заголовок**: Access-Control-Allow-Origin
- **Предварительный запрос**: метод OPTIONS (preflight)
- **Где действует**: только в браузерах
**CORS** (англ. *Cross-Origin Resource Sharing* — совместное использование ресурсов между источниками) — механизм, с помощью которого сервер сообщает браузеру, каким сайтам с других адресов разрешено читать его ответы. По умолчанию браузер запрещает скрипту на странице `https://app.example.com` читать ответ от `https://api.other.com` — это **политика одного источника** (*same-origin policy*). CORS — способ контролируемо ослабить этот запрет с помощью специальных HTTP-заголовков.[1][2] Ошибка «blocked by CORS policy» в консоли браузера — одна из самых частых у веб-разработчиков.

## Политика одного источника

**Источник** (*origin*) — сочетание схемы, хоста и порта.[3]

| Адрес | Тот же источник, что `https://example.com`? |
|---|---|
| `https://example.com/page` | да |
| `http://example.com` | нет — другая схема |
| `https://api.example.com` | нет — другой хост |
| `https://example.com:8443` | нет — другой порт |

Политика защищает пользователя: без неё любой открытый сайт мог бы скриптом отправить запрос в ваш интернет-банк — браузер приложил бы ваши [cookie](https://llmdocs.ru/wiki/cookies-sessions.md) — и прочитать ответ. Важно, что ограничение касается **чтения** ответа скриптом; загрузку картинок, скриптов, отправку форм с других источников политика не запрещает (от подделки таких запросов защищаются иначе, см. [CSRF](https://llmdocs.ru/wiki/csrf.md)).

## Как работает CORS

Браузер добавляет к запросу между источниками заголовок `Origin`, а сервер в ответе указывает, разрешён ли этот источник:

```http
GET /v1/items HTTP/1.1
Host: api.example.com
Origin: https://app.example.com

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.com
Vary: Origin
Content-Type: application/json
```

Если заголовка нет или значение не совпадает, браузер всё равно получает ответ, но **не отдаёт его скрипту** и пишет ошибку в консоль. Сервер при этом запрос уже выполнил — CORS не защищает сервер, а защищает данные пользователя в браузере.

### Простые и предварительные запросы

**Простые запросы** — `GET`, `HEAD` или `POST` с ограниченным набором заголовков и типами тела `text/plain`, `multipart/form-data`, `application/x-www-form-urlencoded` — отправляются сразу. Они не дают ничего, чего нельзя было сделать обычной HTML-формой.

Всё остальное — `PUT`, `DELETE`, `PATCH`, тело `application/json`, заголовок `Authorization` — требует **предварительного запроса** (*preflight*): браузер сначала отправляет `OPTIONS` и спрашивает разрешения.[1]

```http
OPTIONS /v1/items/42 HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: authorization, content-type

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: authorization, content-type
Access-Control-Max-Age: 600
```

`Access-Control-Max-Age` разрешает браузеру кешировать ответ, чтобы не повторять предварительный запрос перед каждым вызовом.

## Заголовки

| Заголовок ответа | Назначение |
|---|---|
| `Access-Control-Allow-Origin` | разрешённый источник или `*` |
| `Access-Control-Allow-Methods` | разрешённые методы (для preflight) |
| `Access-Control-Allow-Headers` | разрешённые заголовки запроса |
| `Access-Control-Allow-Credentials: true` | можно отправлять cookie и HTTP-аутентификацию |
| `Access-Control-Expose-Headers` | какие заголовки ответа доступны скрипту |
| `Access-Control-Max-Age` | время кеширования результата preflight, в секундах |

**Учётные данные.** По умолчанию `fetch` не отправляет cookie на другие источники. Если клиент указал `credentials: "include"`, сервер обязан ответить `Access-Control-Allow-Credentials: true` и **конкретным** источником — подстановочный `*` в этом случае запрещён.

## Пример на сервере

```python
# FastAPI
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://app.example.com"],
    allow_methods=["GET", "POST", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
    allow_credentials=True,
)
```

Готовые решения есть во всех фреймворках (см. [FastAPI](https://llmdocs.ru/wiki/fastapi.md)); заголовки можно добавлять и на уровне [обратного прокси](https://llmdocs.ru/wiki/reverse-proxy.md).

## Частые ошибки

- **Отражение любого `Origin`** в `Access-Control-Allow-Origin` вместе с `Allow-Credentials: true` — фактически отключает защиту: любой сайт сможет читать данные пользователя.[6] Проверяйте источник по точному списку.
- **Проверка по подстроке**: `example.com.evil.net` проходит проверку «содержит example.com».
- **Разрешение источника `null`** — его можно получить из песочницы `iframe` или локального файла.
- **Забытый `Vary: Origin`** при динамическом ответе — [CDN](https://llmdocs.ru/wiki/cdn.md) закеширует ответ для одного источника и отдаст другому.
- **Попытка «починить» CORS на клиенте** — заголовки разрешений выставляет только сервер. Обход через `mode: "no-cors"` даёт непрозрачный ответ, который нельзя прочитать.

Частое и самое простое решение — избежать запросов между источниками вовсе: отдавать фронтенд и API с одного домена, проксируя `/api` на бэкенд.[5]

CORS — механизм браузеров. Серверные программы, `curl` и мобильные приложения его не применяют, поэтому он не заменяет [аутентификацию](https://llmdocs.ru/wiki/authn-authz.md) API.

## См. также

- [HTTP](https://llmdocs.ru/wiki/http.md)
- [Cookie и сессии](https://llmdocs.ru/wiki/cookies-sessions.md)
- [OWASP Top 10](https://llmdocs.ru/wiki/owasp-top-10.md)
- [REST](https://llmdocs.ru/wiki/rest.md)

## Источники

1. [WHATWG Fetch Standard — CORS protocol](https://fetch.spec.whatwg.org/#http-cors-protocol)
2. [MDN — Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS)
3. [MDN — Same-origin policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy)
4. [W3C — Cross-Origin Resource Sharing (Recommendation, 2014)](https://www.w3.org/TR/2014/REC-cors-20140116/)
5. [OWASP — HTML5 Security Cheat Sheet: CORS](https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html#cross-origin-resource-sharing)
6. [PortSwigger Web Security Academy — CORS vulnerabilities](https://portswigger.net/web-security/cors)
