Skip to content
← Все статьи

CORS: полное руководство по Access-Control-Allow

CORS: полное руководство по Access-Control-Allow

CORS (Cross-Origin Resource Sharing) — механизм, которым сервер разрешает браузеру использовать свои ресурсы из другого origin. Без CORS любой JS с чужого домена не может прочитать ответ вашего API документацию — это фундамент Same-Origin Policy. В этой статье разберём preflight-запрос, заголовки Access-Control-Allow-*, работу с credentials и типичные ошибки вида «has been blocked by CORS policy».

Same-Origin Policy и зачем нужен CORS

Origin — это тройка (scheme, host, port). https://app.example.com и https://api.example.com — разные origin. По умолчанию браузер запрещает JS читать ответы cross-origin fetch. CORS — официальный способ сервера сказать: «мне ok, что к этому ресурсу обращаются с другого origin». Стандарт описан в WHATWG Fetch Standard.

Простые запросы

Запрос считается «простым», если метод — GET/POST/HEAD, нет кастомных заголовков, Content-Type — один из application/x-www-form-urlencoded, multipart/form-data, text/plain. Сервер возвращает:

Access-Control-Allow-Origin: https://app.example.com
Vary: Origin

Vary: Origin критичен, если origin динамический — иначе CDN закэширует ответ для первого origin и отдаст его всем остальным.

Preflight OPTIONS

Сложный запрос (PUT/DELETE/PATCH, кастомные заголовки, JSON body) браузер предваряет OPTIONS-запросом. Сервер должен ответить заголовками разрешений:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH
Access-Control-Allow-Headers: Content-Type, Authorization, X-API-Key
Access-Control-Max-Age: 86400

Max-Age кэширует preflight на стороне браузера — уменьшает latency на 50+ мс на каждый запрос.

Credentials (cookies, Authorization)

Чтобы отправить куки или заголовок Authorization, клиент делает fetch(..., { credentials: 'include' }), а сервер — возвращает:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true

Важно: с Allow-Credentials: true НЕЛЬЗЯ использовать Access-Control-Allow-Origin: * — только явный origin. Это защита от кражи данных.

nginx

map $http_origin $cors_origin {
  default "";
  "https://app.example.com" $http_origin;
  "https://admin.example.com" $http_origin;
}

location /api/ {
  if ($request_method = OPTIONS) {
    add_header Access-Control-Allow-Origin $cors_origin always;
    add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH" always;
    add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
    add_header Access-Control-Allow-Credentials "true" always;
    add_header Access-Control-Max-Age "86400" always;
    add_header Vary "Origin" always;
    return 204;
  }
  add_header Access-Control-Allow-Origin $cors_origin always;
  add_header Access-Control-Allow-Credentials "true" always;
  add_header Vary "Origin" always;
  proxy_pass http://backend;
}

Express и NestJS

// Express
import cors from 'cors';
app.use(cors({
  origin: ['https://app.example.com', 'https://admin.example.com'],
  credentials: true,
  methods: ['GET','POST','PUT','DELETE','PATCH'],
  maxAge: 86400,
}));

// NestJS
app.enableCors({
  origin: (origin, cb) => {
    const allow = ['https://app.example.com'];
    if (!origin || allow.includes(origin)) cb(null, true);
    else cb(new Error('CORS blocked'));
  },
  credentials: true,
});

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

«No Access-Control-Allow-Origin header» — сервер не поставил заголовок. Проверить curl -I -H "Origin: https://app.example.com" https://api.example.com/.

«Response has wildcard but credentials include» — либо уберите credentials, либо замените * на явный origin.

Кэш отдаёт неправильный Allow-Origin — добавьте Vary: Origin.

OPTIONS возвращает 401 — preflight идёт без auth-токенов; сделайте исключение в auth middleware для OPTIONS.

Отладка

Проверка в консоли браузера → Network → смотрите preflight и ответ. В терминале:

curl -i -X OPTIONS \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: Content-Type,Authorization" \
  https://api.example.com/endpoint

CORS Checker enterno.io эмулирует произвольный Origin и показывает ответ сервера без кода на фронте.

FAQ

CORS это защита сервера? Нет. CORS — ограничение браузера, которое сервер может ослабить. Curl и любой серверный HTTP-клиент игнорирует CORS.

Можно ли Access-Control-Allow-Origin: *? Только для публичных API без credentials (CDN-ресурсы, open data).

Что блокирует preflight? Чаще всего — отсутствующий Access-Control-Allow-Headers для заголовка Authorization или X-API-Key.

Нужен ли CORS внутри одного origin? Нет — same-origin запросы CORS не трогает.

Вывод

CORS — не антивирус, а декларация. Держите whitelist origin, всегда Vary: Origin, никогда не комбинируйте * + credentials, добавьте мониторинг на CORS-заголовки и проверяйте CORS Checker. Связанные темы: HTTP Security Headers, rate limiting API.

Проверьте ваш сайт прямо сейчас

Проверить →
Другие статьи: SEC
SEC
Защита от XSS атак: типы, эскейпинг, CSP, Trusted Types
15.04.2026 · 3 просм.
SEC
Cookie Security: HttpOnly, Secure, SameSite, __Host-
15.04.2026 · 6 просм.
SEC
CSP (Content Security Policy): настройка с нуля
15.04.2026 · 4 просм.
SEC
Защита от SQL injection: prepared statements и ORM
15.04.2026 · 4 просм.