CORS: Sdílení zdrojů mezi doménami

CORS v kontextu webu: proč existuje a co řeší

CORS (Cross-Origin Resource Sharing) je HTTP mechanismus, který umožňuje bezpečné sdílení zdrojů mezi rozdílnými původy (origins). Opírá se o hlavičky v požadavcích a odpovědích, které prohlížeč respektuje při čtení dat z jiné domény, subdomény, portu nebo protokolu. CORS rozšiřuje tzv. Same-Origin Policy (SOP) tak, aby webové aplikace mohly přistupovat k API a statickým zdrojům napříč doménami bez ztráty bezpečnosti.

Origin a Same-Origin Policy: základní pojmy

  • Origin = kombinace scheme://host:port (např. https://app.example.com:443).
  • Same-Origin Policy brání skriptům načteným z jednoho původu číst citlivé odpovědi z jiného původu.
  • CORS umožní kontrolované prolomení této bariéry přes deklarativní hlavičky na straně serveru.

Aktéři a odpovědnosti

  • Prohlížeč vynucuje pravidla CORS a v případě porušení zablokuje přístup k odpovědi (JS nedostane tělo, konzole vypíše chybu).
  • Server cílového zdroje rozhoduje, komu (kterému původu) a za jakých podmínek poskytne přístup pomocí CORS hlaviček.
  • CDN/Proxy může měnit hlavičky, cachovat preflight odpovědi a ovlivnit chování (pozor na Vary).

Typy požadavků: jednoduché, preflight a credentialed

  • Jednoduché požadavky (bez preflightu): metody GET, HEAD, POST s bezpečnými simple hlavičkami (např. Accept, Content-Type s hodnotami typu application/x-www-form-urlencoded, multipart/form-data nebo text/plain), bez vlastních nestandardních hlaviček.
  • Preflight: prohlížeč nejprve vyšle OPTIONS s hlavičkami Origin, Access-Control-Request-Method a případně Access-Control-Request-Headers. Server musí odpovědět s povolením, jinak hlavní požadavek nebude proveden.
  • Credentialed (s pověřeními): pokud klient posílá cookies/HTTP auth/klientské certifikáty (fetch s credentials: "include"), server musí odpovědět Access-Control-Allow-Credentials: true a nesmí použít Access-Control-Allow-Origin: *; musí uvést konkrétní původ.

Klíčové hlavičky CORS a jejich význam

  • Origin: automaticky přidávaná klientem; identifikuje původ volajícího.
  • Access-Control-Allow-Origin: odpověď serveru s povoleným původem (konkrétní https://example.com nebo * u ne-credentialed požadavků).
  • Access-Control-Allow-Methods: metody povolené při preflightu (GET, POST, PUT, DELETE, OPTIONS…).
  • Access-Control-Allow-Headers: ne-simple hlavičky povolené v hlavním požadavku (např. Authorization, X-Requested-With).
  • Access-Control-Expose-Headers: seznam hlaviček, které mohou být čitelné z JS (bez toho vidí pouze safe list).
  • Access-Control-Allow-Credentials: true pokud jsou povoleny cookies/pověření.
  • Access-Control-Max-Age: čas, během kterého může prohlížeč cachovat výsledek preflightu (snížení latence; prohlížeče mají vlastní horní limity).
  • Vary: Origin: extrémně důležité u CDN – odpověď se má lišit podle Origin. Bez toho hrozí cache poisoning nebo únik hlaviček.

Životní cyklus preflightu krok za krokem

  1. JS (např. fetch) chce provést PUT s hlavičkou Authorization.
  2. Prohlížeč odešle OPTIONS na stejnou URL s hlavičkami Origin a Access-Control-Request-*.
  3. Server odpoví např. Access-Control-Allow-Origin: https://app.example.com, Access-Control-Allow-Methods: PUT, Access-Control-Allow-Headers: Authorization, Access-Control-Max-Age: 600.
  4. Prohlížeč si výsledek preflightu dočasně zapamatuje a provede hlavní požadavek.

Běžné topologie: SPA, API a CDN

  • SPA na doméně A volá API na doméně B: nastavte B tak, aby podle whitelistu odrážel Access-Control-Allow-Origin pouze pro známé původy a přidal Vary: Origin.
  • CDN před API: nechte CDN respektovat a přeposílat Origin na původní server; cachujte bezpečně podle Vary a zvažte cachování preflightů.
  • Statické assety (fonty, obrázky, video): pokud se používají napříč doménami (např. fonty), přidejte Access-Control-Allow-Origin a správný Content-Type.

Konfigurace: návrhové vzory bez bezpečnostních děr

  • Reflexe původu s whitelistem: server zkontroluje Origin, pokud je v seznamu, vrátí Access-Control-Allow-Origin s touto hodnotou a doplní Vary: Origin. Nikdy nereflektujte libovolný Origin bez kontroly.
  • Credentialed API: použijte konkrétní Access-Control-Allow-Origin (nikoli *) a Access-Control-Allow-Credentials: true. Omezte Allow-Methods a Allow-Headers na minimum.
  • Minimalizace preflightu: preferujte simple metody a hlavičky; posílejte JSON jako text/plain pouze pokud to bezpečnostní politika dovoluje a víte, co děláte. Častěji je lepší akceptovat preflight a správně ho cachovat.

Příklady hlaviček pro API odpovědi

Ne-credentialed, více domén přes CDN: Access-Control-Allow-Origin: *, Access-Control-Expose-Headers: ETag, Link, Vary: Origin (pokud později přejdete na per-origin politiku).

Credentialed pouze pro app: Access-Control-Allow-Origin: https://app.example.com, Access-Control-Allow-Credentials: true, Access-Control-Expose-Headers: X-RateLimit-Remaining, Vary: Origin.

Preflight odpověď: Access-Control-Allow-Origin: https://app.example.com, Access-Control-Allow-Methods: GET, POST, Access-Control-Allow-Headers: Authorization, Content-Type, Access-Control-Max-Age: 600, Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers.

Redirecty, cache a CORS

  • Přesměrování mohou narušit CORS, pokud mezikrok odstraní hlavičky nebo změní protokol/host/port. Ideálně odpovídejte přímo bez 302/301, zejména na preflight.
  • CDN cache: vždy uvažujte Vary na Origin a potenciálně i na Access-Control-Request-* u preflightu.
  • ETag a podmíněné požadavky (If-None-Match) fungují s CORS, ale pouze pokud se zachovají CORS hlavičky i u 304.

Bezpečnostní souvislosti: CORS ≠ autentizace

  • CORS je mechanismus prohlížeče; nenahrazuje autentizaci ani autorizaci. Server musí stále validovat tokeny, session a ACL.
  • Nikdy nepovolujte * spolu s Allow-Credentials: true. Prohlížeče to blokují; jde i o bezpečnostní princip.
  • Oddělte CORS od politik CSP, COOP/COEP/CORP – tyto související mechanismy řeší jiné vektory (izolace, čitelnost, cross-origin embedding).

Vliv na SEO, AIO/AEO a LLM optimalizaci

  • Indexace a rendering: Googlebot s Web Rendering Service sice vykonává JS, ale API volání přes CORS mohou selhat a způsobit neúplný obsah. Důležitá data pro SEO (např. produktové texty, FAQ) raději renderujte SSR/SSG a nevazujte je na runtime CORS volání.
  • Structured Data: netahajte JSON-LD přes cross-origin runtime fetch; vložte ho přímo do HTML.
  • AIO/AEO (odpovědi asistentů): pokud widgety (HowTo, FAQ, Product) závisí na cross-origin médiích (obrázky, video), povolte CORS na asset doméně, jinak embed komponenty v SPA mohou zobrazit „broken“ stav.
  • Výkon: preflight přidává RTT; při vysokém podílu CORS volání roste TTI a zhoršují se Core Web Vitals. Zvažte slučování API, HTTP/2/3, cachování preflightu a same-origin proxy.

Diagnostika a testování

  • DevTools → Síť: filtrujte OPTIONS, zkontrolujte přítomnost správných CORS hlaviček na preflight i na hlavní odpovědi.
  • curl: simulujte preflight např. pomocí curl -i -X OPTIONS https://api.example.com/resource -H "Origin: https://app.example.com" -H "Access-Control-Request-Method: PUT" -H "Access-Control-Request-Headers: Authorization".
  • Logy serveru/CDN: zapněte logování pro OPTIONS, sledujte cache hit/miss a chování Vary.
  • Monitoring: měřte procento CORS chyb v JS (window error handler) a korelujte s konverzemi.

Nejčastější chyby a jejich řešení

  1. „No ‚Access-Control-Allow-Origin‘ header is present“ – server nevrací povolení pro daný původ; přidejte správný Access-Control-Allow-Origin a Vary: Origin.
  2. „The value of the ‚Access-Control-Allow-Origin‘ header contains ‚*‘ when credentials flag is true“ – použijte konkrétní původ místo * a ponechte Allow-Credentials: true.
  3. Preflight blokován 301/302 – odpovídejte přímo nebo přesměrujte pouze hlavní požadavek; preflight by měl dostat definitivní odpověď s CORS hlavičkami.
  4. Chybějící hlavičky u 304 – i 304 musí obsahovat relevantní CORS hlavičky.
  5. CDN odstraňuje hlavičky – povolte a přeposílejte Origin a zachovejte CORS hlavičky na edge.
  6. Příliš široké Allow-Headers/Methods – zužujte seznam na nezbytné hodnoty, snižujete útokový povrch.

Optimalizace výkonu: jak snížit CORS latenci

  • Sláčujte volání a preferujte GET pro čitelné zdroje s agresivním cache-control.
  • Pro preflight použijte rozumné Access-Control-Max-Age a zvažte jeho cachování na CDN.
  • Přesuňte API pod stejný původ (reverse proxy, /api na téže doméně), kde je to možné.
  • Minimalizujte vlastní hlavičky; Authorization je častý spouštěč preflightu – zvažte alternativní modely (např. cookie s SameSite podle potřeby a CSRF ochranou), ale vždy s ohledem na bezpečnost.

Specifika cookies a SameSite

  • Pro cross-site cookies je často potřeba SameSite=None; Secure. Jinak prohlížeč cookie nepošle, i když CORS povoluje pověření.
  • Nezapomeňte sladit doménu cookie (Domain) s tím, jak ji chcete používat napříč subdoménami.

Fonty, obrázky a média napříč doménami

  • Web fonty vyžadují správné CORS hlavičky na asset doméně (Access-Control-Allow-Origin), jinak mohou být zablokovány.
  • Při <img> načtení proběhne, ale čtení pixelových dat z canvasu bez CORS vede k taintnutému canvasu (ochrana soukromí).
  • Video a audio streamy mohou vyžadovat kompatibilní CORS i pro Range požadavky a správné MIME typy.

Integrace do vývojového procesu

  • Infra jako kód: definujte CORS politiky v Nginx/Apache/CloudFront/S3 konfiguraci jako součást repozitáře.
  • Testy: přidejte integrační testy, které ověřují přítomnost správných hlaviček pro běžné i preflight scénáře.
  • Observabilita: metriky pro OPTIONS traffic, chybovost a preflight cache hit rate na CDN.

Checklist pro bezpečné zavedení CORS

  • Máte explicitní seznam povolených původů (včetně wildcardů pro subdomény,