[{"data":1,"prerenderedAt":538},["ShallowReactive",2],{"guide-url-origin-cors-security":3},{"id":4,"title":5,"body":6,"date":528,"description":529,"extension":530,"meta":531,"navigation":532,"path":533,"readingTime":534,"seo":535,"stem":536,"__hash__":537},"guides\u002Fguides\u002Furl-origin-cors-security.md","URL Origin and CORS Security",{"type":7,"value":8,"toc":509},"minimark",[9,13,18,34,45,48,158,169,173,176,183,200,210,215,222,279,282,286,289,293,300,306,309,315,326,330,337,343,346,352,355,359,363,369,375,379,385,406,409,413,419,423,433,461,465,492,496,505],[10,11,12],"p",{},"Cross-Origin Resource Sharing (CORS) is the browser security mechanism that governs whether a web page can make requests to a different origin. Understanding how browsers determine \"same-origin\" — and how CORS policies allow or block cross-origin requests — is essential for any developer building APIs, SPAs, or microservice architectures.",[14,15,17],"h2",{"id":16},"what-is-an-origin","What Is an Origin",[10,19,20,21,25,26,29,30,33],{},"An origin is defined by three URL components: ",[22,23,24],"strong",{},"scheme",", ",[22,27,28],{},"host",", and ",[22,31,32],{},"port",".",[35,36,41],"pre",{"className":37,"code":39,"language":40},[38],"language-text","https:\u002F\u002Fapi.example.com:443\u002Fdata\n  ↑        ↑              ↑\nscheme    host           port\n","text",[42,43,39],"code",{"__ignoreMap":44},"",[10,46,47],{},"Two URLs share the same origin if and only if all three components match exactly.",[49,50,51,70],"table",{},[52,53,54],"thead",{},[55,56,57,61,64,67],"tr",{},[58,59,60],"th",{},"URL A",[58,62,63],{},"URL B",[58,65,66],{},"Same Origin?",[58,68,69],{},"Reason",[71,72,73,92,110,126,142],"tbody",{},[55,74,75,81,86,89],{},[76,77,78],"td",{},[42,79,80],{},"https:\u002F\u002Fexample.com\u002Fa",[76,82,83],{},[42,84,85],{},"https:\u002F\u002Fexample.com\u002Fb",[76,87,88],{},"Yes",[76,90,91],{},"Same scheme, host, port",[55,93,94,99,104,107],{},[76,95,96],{},[42,97,98],{},"https:\u002F\u002Fexample.com",[76,100,101],{},[42,102,103],{},"http:\u002F\u002Fexample.com",[76,105,106],{},"No",[76,108,109],{},"Different scheme",[55,111,112,116,121,123],{},[76,113,114],{},[42,115,98],{},[76,117,118],{},[42,119,120],{},"https:\u002F\u002Fapi.example.com",[76,122,106],{},[76,124,125],{},"Different host",[55,127,128,132,137,139],{},[76,129,130],{},[42,131,98],{},[76,133,134],{},[42,135,136],{},"https:\u002F\u002Fexample.com:8080",[76,138,106],{},[76,140,141],{},"Different port",[55,143,144,148,153,155],{},[76,145,146],{},[42,147,98],{},[76,149,150],{},[42,151,152],{},"https:\u002F\u002Fexample.com:443",[76,154,88],{},[76,156,157],{},"443 is default for HTTPS",[10,159,160,161,164,165,168],{},"The path, query string, and fragment do not affect origin. ",[42,162,163],{},"\u002Fa"," and ",[42,166,167],{},"\u002Fb"," on the same origin are same-origin by definition.",[14,170,172],{"id":171},"the-same-origin-policy","The Same-Origin Policy",[10,174,175],{},"The same-origin policy (SOP) is the default rule: a script running on one origin cannot read responses from a different origin. This prevents malicious sites from stealing data from authenticated sessions on other sites.",[10,177,178,179,182],{},"Without SOP, if you visited ",[42,180,181],{},"evil.com",", its JavaScript could:",[184,185,186,194,197],"ol",{},[187,188,189,190,193],"li",{},"Make a fetch to ",[42,191,192],{},"bank.com\u002Fapi\u002Fbalance"," (your browser includes your bank cookies)",[187,195,196],{},"Read the response",[187,198,199],{},"Exfiltrate your balance to the attacker",[10,201,202,203,205,206,209],{},"SOP blocks step 2 — the script on ",[42,204,181],{}," cannot read the response from ",[42,207,208],{},"bank.com"," because the origins differ.",[211,212,214],"h3",{"id":213},"what-sop-allows","What SOP Allows",[10,216,217,218,221],{},"SOP does not block all cross-origin traffic. It specifically blocks ",[22,219,220],{},"reading"," cross-origin responses. The following are still allowed:",[223,224,225,234,240,259],"ul",{},[187,226,227,230,231],{},[22,228,229],{},"Cross-origin navigations",": clicking a link or setting ",[42,232,233],{},"window.location",[187,235,236,239],{},[22,237,238],{},"Cross-origin form submissions",": posting form data to another origin",[187,241,242,245,246,25,249,25,252,25,255,258],{},[22,243,244],{},"Embedded resources",": ",[42,247,248],{},"\u003Cimg>",[42,250,251],{},"\u003Cscript>",[42,253,254],{},"\u003Clink>",[42,256,257],{},"\u003Cvideo>"," tags loading cross-origin content",[187,260,261,245,264,164,267,270,271,274,275,278],{},[22,262,263],{},"Cross-origin writes",[42,265,266],{},"POST",[42,268,269],{},"PUT"," requests via ",[42,272,273],{},"fetch"," or ",[42,276,277],{},"XMLHttpRequest"," (the request is sent, but the response cannot be read)",[10,280,281],{},"This distinction is crucial: SOP prevents data theft, not data sending. CSRF attacks exploit the \"writes allowed\" gap.",[14,283,285],{"id":284},"how-cors-works","How CORS Works",[10,287,288],{},"CORS is a relaxation of SOP — a way for servers to explicitly allow specific origins to read responses. It uses HTTP headers to negotiate access.",[211,290,292],{"id":291},"simple-requests","Simple Requests",[10,294,295,296,299],{},"A \"simple\" request (GET, HEAD, or POST with limited headers) is sent directly. The browser adds an ",[42,297,298],{},"Origin"," header:",[35,301,304],{"className":302,"code":303,"language":40},[38],"GET \u002Fapi\u002Fdata HTTP\u002F1.1\nHost: api.example.com\nOrigin: https:\u002F\u002Ffrontend.example.com\n",[42,305,303],{"__ignoreMap":44},[10,307,308],{},"The server responds with CORS headers:",[35,310,313],{"className":311,"code":312,"language":40},[38],"HTTP\u002F1.1 200 OK\nAccess-Control-Allow-Origin: https:\u002F\u002Ffrontend.example.com\nAccess-Control-Allow-Credentials: true\nContent-Type: application\u002Fjson\n",[42,314,312],{"__ignoreMap":44},[10,316,317,318,321,322,325],{},"If ",[42,319,320],{},"Access-Control-Allow-Origin"," matches the requesting origin (or is ",[42,323,324],{},"*","), the browser allows the script to read the response. If the header is missing or does not match, the browser blocks the response.",[211,327,329],{"id":328},"preflight-requests","Preflight Requests",[10,331,332,333,336],{},"For non-simple requests (custom headers, PUT\u002FDELETE methods, JSON content type), the browser sends a preflight ",[42,334,335],{},"OPTIONS"," request first:",[35,338,341],{"className":339,"code":340,"language":40},[38],"OPTIONS \u002Fapi\u002Fdata HTTP\u002F1.1\nHost: api.example.com\nOrigin: https:\u002F\u002Ffrontend.example.com\nAccess-Control-Request-Method: PUT\nAccess-Control-Request-Headers: Content-Type, X-Auth-Token\n",[42,342,340],{"__ignoreMap":44},[10,344,345],{},"The server responds with allowed methods and headers:",[35,347,350],{"className":348,"code":349,"language":40},[38],"HTTP\u002F1.1 204 No Content\nAccess-Control-Allow-Origin: https:\u002F\u002Ffrontend.example.com\nAccess-Control-Allow-Methods: GET, PUT, DELETE\nAccess-Control-Allow-Headers: Content-Type, X-Auth-Token\nAccess-Control-Max-Age: 86400\n",[42,351,349],{"__ignoreMap":44},[10,353,354],{},"Only after the preflight succeeds does the browser send the actual request.",[14,356,358],{"id":357},"common-cors-mistakes","Common CORS Mistakes",[211,360,362],{"id":361},"wildcard-with-credentials","Wildcard with Credentials",[35,364,367],{"className":365,"code":366,"language":40},[38],"Access-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\n",[42,368,366],{"__ignoreMap":44},[10,370,371,372,374],{},"This combination is invalid. Browsers reject it because ",[42,373,324],{}," with credentials would allow any site to make authenticated requests. If you need credentials, you must specify the exact origin.",[211,376,378],{"id":377},"reflecting-the-origin-header-naively","Reflecting the Origin Header Naively",[10,380,381,382,384],{},"Some servers echo back whatever ",[42,383,298],{}," header they receive:",[35,386,390],{"className":387,"code":388,"language":389,"meta":44,"style":44},"language-python shiki shiki-themes github-light github-dark","# Dangerous!\nresponse.headers['Access-Control-Allow-Origin'] = request.headers.get('Origin')\n","python",[42,391,392,400],{"__ignoreMap":44},[393,394,397],"span",{"class":395,"line":396},"line",1,[393,398,399],{},"# Dangerous!\n",[393,401,403],{"class":395,"line":402},2,[393,404,405],{},"response.headers['Access-Control-Allow-Origin'] = request.headers.get('Origin')\n",[10,407,408],{},"This effectively disables SOP — any origin can read responses. Always validate the origin against an allowlist before reflecting it.",[211,410,412],{"id":411},"missing-preflight-handling","Missing Preflight Handling",[10,414,415,416,418],{},"If your API does not respond to ",[42,417,335],{}," requests, preflight checks fail, and browsers block the actual request. Ensure your server or API gateway handles OPTIONS for all CORS-enabled routes.",[211,420,422],{"id":421},"split-stack-anti-pattern","Split Stack Anti-Pattern",[10,424,425,426,164,429,432],{},"When the frontend and API run on different origins (e.g., ",[42,427,428],{},"app.example.com",[42,430,431],{},"api.example.com","), every API call is cross-origin. This is the most common source of CORS issues. Solutions:",[223,434,435,441,451],{},[187,436,437,440],{},[22,438,439],{},"Same-origin proxy",": serve the API behind the same origin using a reverse proxy (Nginx, Cloudflare Workers)",[187,442,443,446,447,450],{},[22,444,445],{},"Subdomain CORS",": allow ",[42,448,449],{},"https:\u002F\u002Fapp.example.com"," explicitly on the API server",[187,452,453,456,457,460],{},[22,454,455],{},"Cookie domain",": set cookies on ",[42,458,459],{},".example.com"," to share auth across subdomains",[14,462,464],{"id":463},"key-takeaways","Key Takeaways",[223,466,467,470,473,476,486],{},[187,468,469],{},"Origin is defined by scheme + host + port — path and query do not matter.",[187,471,472],{},"The same-origin policy blocks reading cross-origin responses, not sending cross-origin requests.",[187,474,475],{},"CORS headers let servers opt in to cross-origin reads; without them, browsers block responses.",[187,477,478,479,482,483,33],{},"Never combine ",[42,480,481],{},"Access-Control-Allow-Origin: *"," with ",[42,484,485],{},"Access-Control-Allow-Credentials: true",[187,487,488,489,491],{},"Always validate the ",[42,490,298],{}," header against an allowlist — never blindly reflect it.",[14,493,495],{"id":494},"try-it-yourself","Try It Yourself",[10,497,498,499,504],{},"Inspecting URL components to debug a CORS issue? The ",[500,501,503],"a",{"href":502},"\u002Ftools\u002Furl-parser","URL Parser"," dissects any URL into scheme, host, port, path, and query — so you can quickly identify origin mismatches.",[506,507,508],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":44,"searchDepth":402,"depth":402,"links":510},[511,512,516,520,526,527],{"id":16,"depth":402,"text":17},{"id":171,"depth":402,"text":172,"children":513},[514],{"id":213,"depth":515,"text":214},3,{"id":284,"depth":402,"text":285,"children":517},[518,519],{"id":291,"depth":515,"text":292},{"id":328,"depth":515,"text":329},{"id":357,"depth":402,"text":358,"children":521},[522,523,524,525],{"id":361,"depth":515,"text":362},{"id":377,"depth":515,"text":378},{"id":411,"depth":515,"text":412},{"id":421,"depth":515,"text":422},{"id":463,"depth":402,"text":464},{"id":494,"depth":402,"text":495},"2026-05-28","How browsers determine same-origin from URL components and enforce CORS policies.","md",{"immutable":532},true,"\u002Fguides\u002Furl-origin-cors-security",6,{"title":5,"description":529},"guides\u002Furl-origin-cors-security","K4BAd1HPrZp0i0d1_6HAscliGi2S8p6_E0tXYWlgsc0",1780401337986]