CORS HTTP hlavičky

Cross-Origin Resource Sharing (CORS) je mechanismus, který umožňuje webovým stránkám bezpečně přistupovat ke zdrojům z jiné domény, než ze které byly načteny. Bez CORS by prohlížeč takové požadavky blokoval kvůli politice Same-Origin Policy (SOP), která je jedním ze základních pilířů bezpečnosti webu.

V tomto článku si vysvětlíme, jak CORS funguje na úrovni HTTP, projdeme si všechny důležité hlavičky, ukážeme si rozdíl mezi jednoduchými a tzv. preflight požadavky a nakonec implementujeme robustní řešení v PHP.


Co je Same-Origin Policy a proč CORS existuje

Než se ponoříme do CORS, je potřeba pochopit, proti čemu vlastně chrání. Same-Origin Policy říká, že JavaScript běžící na doméně example.com nemůže standardně číst odpovědi z požadavků směřujících na api.jine-domene.com. Origin je definován trojicí:

  • schéma (http vs https)
  • hostname (doména)
  • port

Pokud se jakákoli z těchto tří částí liší, jedná se o cross-origin požadavek.

Kdyby SOP neexistovala, mohla by škodlivá stránka, kterou si otevřete, na pozadí poslat požadavek například na vaši internetovou banku — s vašimi cookies — a přečíst si odpověď. CORS je řízenou výjimkou z této politiky: server může explicitně povolit konkrétním cizím doménám přístup ke svým zdrojům.

Důležité: CORS je vynucován prohlížečem, nikoli serverem. Server odpověď stejně pošle, ale prohlížeč ji JavaScriptu nezpřístupní, pokud chybí správné hlavičky. Nástroje jako curl nebo serverové HTTP klienty CORS ignorují.


Jednoduché vs. preflight požadavky

CORS rozlišuje dva typy požadavků a chování prohlížeče se mezi nimi výrazně liší.

Jednoduché požadavky (simple requests)

Požadavek je považován za "jednoduchý", pokud splňuje všechny následující podmínky:

  • Metoda je GET, HEAD nebo POST
  • Hlavičky obsahují pouze tzv. CORS-safelisted hodnoty (Accept, Accept-Language, Content-Language, Content-Type)
  • Content-Type má hodnotu pouze application/x-www-form-urlencoded, multipart/form-data nebo text/plain

V tomto případě prohlížeč pošle požadavek rovnou a teprve podle odpovědi rozhodne, zda ji JavaScriptu zpřístupní.

Preflight požadavky

Pokud požadavek nesplňuje podmínky pro jednoduchý — typicky používá PUT, DELETE, PATCH nebo posílá JSON s Content-Type: application/json — prohlížeč nejprve pošle preflight požadavek metodou OPTIONS. Tím se serveru zeptá, zda je vůbec možné požadovanou operaci provést.

Schéma preflight komunikace:

Prohlížeč → Server: OPTIONS /api/users
Origin: https://moje-aplikace.cz
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Content-Type, Authorization
Server → Prohlížeč: HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://moje-aplikace.cz
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Prohlížeč → Server: DELETE /api/users/42
(skutečný požadavek)

Až po úspěšném preflightu prohlížeč pošle skutečný požadavek.


Hlavní CORS hlavičky

Hlavička Účel
Access-Control-Allow-Origin Určuje, které originy mají přístup. Hodnota * povoluje všem, jinak konkrétní URL.
Access-Control-Allow-Credentials Pokud je true, prohlížeč povolí poslat cookies a HTTP autentizaci.
Access-Control-Allow-Methods Seznam povolených HTTP metod (např. GET, POST, PUT, DELETE). Používá se v odpovědi na preflight.
Access-Control-Allow-Headers Hlavičky, které smí klient v požadavku použít.
Access-Control-Expose-Headers Hlavičky, které smí JavaScript číst z odpovědi (jinak vidí jen základní set).
Access-Control-Max-Age Jak dlouho (v sekundách) si prohlížeč může cachovat výsledek preflightu.

Pozor na kombinaci wildcard a credentials

Toto je nejčastější chyba, na kterou vývojáři narazí:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

Tato kombinace nefunguje. Pokud chcete posílat cookies (credentials: 'include' ve fetch API), musíte vrátit konkrétní origin, ne hvězdičku. Prohlížeč v opačném případě požadavek odmítne.


Implementace CORS v PHP

Následující implementace pokrývá nejčastější scénáře a ošetřuje i bezpečnostní úskalí. Místo slepého echování HTTP_ORIGIN zpět používá whitelist povolených domén.

php
final class CorsHandler
{
/** @var string[] Povolené originy — nikdy nedůvěřujte slepě tomu, co přijde od klienta */
private const ALLOWED_ORIGINS = [
'https://moje-aplikace.cz',
'https://www.moje-aplikace.cz',
'http://localhost:3000', // pro lokální vývoj
];
private const ALLOWED_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
private const ALLOWED_HEADERS = ['Content-Type', 'Authorization', 'X-Requested-With'];
private const MAX_AGE = 86400; // 24 hodin
public static function handle(): void
{
$origin = $_SERVER['HTTP_ORIGIN'] ?? null;
// Origin neznáme nebo není v whitelistu — neposíláme žádné CORS hlavičky
if ($origin === null || !in_array($origin, self::ALLOWED_ORIGINS, true)) {
return;
}
header('Access-Control-Allow-Origin: ' . $origin);
header('Access-Control-Allow-Credentials: true');
header('Vary: Origin'); // důležité pro caching proxy
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
if ($method === 'OPTIONS') {
header('Access-Control-Allow-Methods: ' . implode(', ', self::ALLOWED_METHODS));
header('Access-Control-Allow-Headers: ' . implode(', ', self::ALLOWED_HEADERS));
header('Access-Control-Max-Age: ' . self::MAX_AGE);
http_response_code(204);
exit;
}
}
}
// Použití na začátku entry pointu (např. index.php)
CorsHandler::handle();

Co tato implementace dělá lépe oproti naivnímu řešení

  1. Whitelist místo echa. Slepé vracení Origin hlavičky útočníkovi efektivně vypíná CORS ochranu — útočníkův prohlížeč prostě dostane tu hlavičku, kterou si přeje.
  2. Hlavička Vary: Origin zajistí, že caching proxy (CDN, reverse proxy) neuloží odpověď s CORS hlavičkami pro jednu doménu a pak ji nepošle požadavku z jiné.
  3. HTTP status 204 No Content je sémanticky správnější odpověď na preflight než výchozí 200.
  4. Access-Control-Max-Age snižuje počet preflight požadavků a zlepšuje výkon API.

Když používáte framework

Většina moderních PHP frameworků má CORS middleware už hotový:

  • Symfony — balíček nelmio/cors-bundle
  • Laravel — vestavěný HandleCors middleware
  • Slim / Mezzio — middleware tuupola/cors-middleware

Vlastní implementaci pište jen tehdy, kdy nepoužíváte framework nebo máte specifické požadavky.


Časté chyby a jejich diagnostika

"CORS error" v konzoli prohlížeče

Pokud v DevTools vidíte chybu typu "has been blocked by CORS policy", projděte si tento checklist:

  1. Vrací server hlavičku Access-Control-Allow-Origin? Zkontrolujte odpověď v záložce Network.
  2. Sedí origin přesně? https://example.com a https://www.example.com jsou různé originy.
  3. Posíláte cookies a máte konkrétní origin? Wildcard * s credentials: true nefunguje.
  4. Selhal preflight? Pokud OPTIONS požadavek vrátí 4xx/5xx, skutečný požadavek se vůbec nepošle.
  5. Není problém na proxy/CDN? Některé reverse proxy strippují CORS hlavičky, jiné je naopak duplikují.

CORS není autorizace

Nejdůležitější věc na závěr: CORS není bezpečnostní mechanismus pro server. Chrání pouze uživatele prohlížeče před nežádoucím čtením dat skripty z jiných domén. Server musí mít vlastní autentizaci a autorizaci — typicky tokeny, session, OAuth — protože nic nebrání útočníkovi v poslání požadavku přímo z curl nebo z vlastního backendu.


Závěr

CORS je elegantní řešení problému, který vznikl tím, jak otevřeným ekosystémem se web stal. Pochopení rozdílu mezi jednoduchými a preflight požadavky, znalost klíčových hlaviček a vědomí, že CORS chrání prohlížeč a ne server, vám ušetří hodiny ladění "záhadných" chyb.

Při návrhu API si dopředu rozmyslete, kdo k němu má mít přístup, používejte whitelist místo wildcardu, nezapomeňte na Vary: Origin a tam, kde můžete, sáhněte po vyzkoušeném middleware z frameworku.

AI konzultace & implementace

Pomohu vám zavést AI do vašeho projektu– od analýzy a návrhu řešení až po nasazení do produkce. Ušetřete čas, zjednodušte procesy a získejte konkurenční výhodu.

Domluvit konzultaci
Konzultant: Jan Barášek
Potřebujete poradit s PHP?

Nabízím trénink vývojářů, konzultace, školení a analýzu návrhových vzorů. Osobně v Praze nebo online.

Napište mi

Newsletter

Nejlepsi tipy a triky o PHP do Vaseho e-mailu. Clanky a novinky nejen ze sveta PHP a programovani.