{"id":15546053,"url":"https://github.com/vasuadari/learn_cors","last_synced_at":"2026-01-08T01:04:02.226Z","repository":{"id":82227702,"uuid":"266748006","full_name":"vasuadari/learn_cors","owner":"vasuadari","description":"A quick guide to understand Cross-Origin Resource Sharing.","archived":false,"fork":false,"pushed_at":"2020-06-01T20:16:15.000Z","size":12,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-09T13:02:55.371Z","etag":null,"topics":["ajax","cors","cors-requests","cors-safelisted","cross-origin-resource-sharing","preflight-requests","xhr"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vasuadari.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-25T10:14:59.000Z","updated_at":"2023-10-24T05:55:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"82309fc4-0b33-48ff-b4df-8efc427360bf","html_url":"https://github.com/vasuadari/learn_cors","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vasuadari%2Flearn_cors","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vasuadari%2Flearn_cors/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vasuadari%2Flearn_cors/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vasuadari%2Flearn_cors/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vasuadari","download_url":"https://codeload.github.com/vasuadari/learn_cors/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246122391,"owners_count":20726823,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ajax","cors","cors-requests","cors-safelisted","cross-origin-resource-sharing","preflight-requests","xhr"],"created_at":"2024-10-02T13:00:34.054Z","updated_at":"2026-01-08T01:04:02.182Z","avatar_url":"https://github.com/vasuadari.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cross-Origin Resource Sharing (CORS)\n\n## Contents\n1. [Pre-Requisites](#1-pre-requisites)\n2. [Why do we need CORS?](#2-why-do-we-need-cors?)\n3. [Introduction](#3-introduction)\n4. [Basics](#4-basics)\n    - 4.1. [Request Headers](#41-request-headers)\n      - 4.1.1. [Origin](#411-origin)\n      - 4.1.2. [Access-Control-Request-Method](#412-access-control-request-method)\n      - 4.1.3. [Access-Control-Request-Headers](#413-access-control-request-headers)\n      - 4.1.4. [CORS-safelisted](#414-cors-safelisted)\n    - 4.2. [Response Headers](#42-response-headers)\n      - 4.2.1. [Access-Control-Allow-Origin](#421-access-control-allow-origin)\n      - 4.2.2. [Access-Control-Allow-Methods](#422-access-control-allow-methods)\n      - 4.2.3. [Access-Control-Allow-Headers](#423-access-control-allow-headers)\n      - 4.2.4. [Access-Control-Max-Age](#424-access-control-max-age)\n      - 4.2.5. [Vary](#425-vary)\n    - 4.3. [Simple Requests](#43-simple-requests)\n    - 4.4. [Preflight Requests](#44-preflight-requests)\n    - 4.5. [Request with credentials](#45-request-with-credentials)\n5. [Advanced Topics](#5-advanced-topics)\n    - 5.1. [Additional HTTP Response Headers](#51-additional-http-response-headers)\n      - 5.1.1. [Access-Control-Expose-Headers](#511-access-control-expose-headers)\n      - 5.1.2. [Access-Control-Allow-Credentials](#512-access-control-allow-credentials)\n    - 5.2. [Credentialed requests and wildcards](#52-credentialed-requests-and-wildcards)\n    - 5.3. [Preflighted requests and redirects](#53-preflighted-requests-and-redirects)\n    - 5.4. [Third-party cookies](#54-third-party-cookies)\n6. [TODO](#6-todo)\n7. [References](#7-references)\n\n## 1. Pre-Requisites\n\nKnow difference between [Simple HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview) and [XHR](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) request.\n\n## 2. Why do we need CORS?\n\nOrigin Web App - https://github.com\n\nWhen you open a website by typing Origin Web App, your browser makes Simple HTTP requests. CORS is not applicable here.\nAs we are voluntarily requesting to serve content from Origin Web App. Your browser will/should serve it without any\ntrouble. But, when it comes to XHR requests, JavaScript which is loaded by Origin Web App in your browser can make a\nrequest to any server(https://netflix.com) requesting for a resource. Now, Origin Web App is owned by Github\nbut https://netflix.com is owned by Netflix, that server could pontentially serve anything. Github cannot take control\nof a server which is owned by Netflix. This has lot of security implications pontentially stealing a content from one\nwebsite(it could be a financial website) to any remote server.\n\nLuckily, CORS tackles this problem very well with a given set of rules.\n\n## 3. Introduction\n\nIts a standard defined by [W3C](https://en.wikipedia.org/wiki/World_Wide_Web_Consortium) to enable cross-origin requests between client(browser) and the server to share\nresources at the same time maintaining security. Any browser will comply with these standards to prevent\nloading resources from any third-party servers.\n\n## 4. Basics\n\n### 4.1. Request Headers\n\n#### 4.1.1. Origin\n\nA header indicates where a request originates from. It is sent with CORS requests,\nas well as with POST requests.\n\n**Syntax:**\n\n```\nOrigin: \u003cscheme\u003e \"://\" \u003chostname\u003e [\":\" \u003cport\u003e ]\n```\n\n**Examples:**\n\n```\nOrigin: https://netflix.com\nOrigin: http://netflix.com:443\nOrigin: http://localhost:1443\n```\n\n#### 4.1.2. Access-Control-Request-Method\n\nThis header is used by browser when issuing a [pre-flight](#44-preflight-requests) requests to indicate which\nrequest method will be used when the actual request is made.\n\n**Syntax:**\n\n```\nAccess-Control-Request-Method: \u003cmethod\u003e\n```\n\n`\u003cmethod\u003e` could be either `GET`, `POST` or `DELETE`.\n\n**Example:**\n\n```\nAccess-Control-Request-Method: POST\n```\n\n#### 4.1.3. Access-Control-Request-Headers\n\nThis header is again used in the [pre-flight](#44-preflight-requests) requests by browser to indicate which\nrequest headers are to be used when the actual request is made. To use multiple headers,\nit has to be separated with comma.\n\n**Syntax:**\n\n```\nAccess-Control-Request-Headers: \u003cheader-name\u003e\nAccess-Control-Request-Headers: \u003cheader-name\u003e, \u003cheader-name\u003e\n```\n\n**Example:**\n\n```\nAccess-Control-Request-Headers: X-PINGOTHER, Content-Type\n```\n\n**Important:** All the header should be [CORS-safelisted](#414-cors-safelisted) or a custom header like `X-Custom-Header`.\n\n#### 4.1.4. CORS-safelisted\n\n- `Accept`\n\n- `Accept-Language`\n\n- `Content-Language`\n\n- `Content-Type`\n\n  Allowed values are `application/x-www-form-urlencoded`, `multipart/form-data` and\n  `text/plain`.\n\n### 4.2. Response Headers\n\nThe following headers are returned in the [pre-flight](#44-preflight-requests) requests.\n\n#### 4.2.1. Access-Control-Allow-Origin\n\nThis header indicates that if the requested [Origin](#411-origin) is allowed. Your browser will\nchoose to succeed/fail the request by matching the requested origin with this.\n\n**Syntax:**\n\n```\nAccess-Control-Allow-Origin: *\n```\n\nFor requests without [credentials](#45-request-with-credentials), the value `*` can be specified as a wildcard.\nThis tells your browser to allow requests from any [Origin](#411-origin).\n\n```\nAccess-Control-Allow-Origin: \u003corigin\u003e\n```\n\nWhen you receive only one origin in the response header, it means your server/web-app\nbased on the requested `Origin` it responds with same `Origin` if its allowed.\nYour server should also respond with [Vary](#425-vary) to indicate that\nit varies based on a request header.\n\n**Example:**\n\n```\nAccess-Control-Allow-Origin: https://github.com\n```\n\n#### 4.2.2. Access-Control-Allow-Methods\n\nYour browser will make actual request if one of the value in this header matches.\nWhen wildcard `*` is returned, it means any method is allowed.\n\n**Syntax:**\n\n```\nAccess-Control-Allow-Methods: \u003cmethod\u003e, \u003cmethod\u003e, ...\nAccess-Control-Allow-Methods: *\n```\n\n**Example:**\n\n```\nAccess-Control-Allow-Methods: GET, POST\n```\n\n#### 4.2.3. Access-Control-Allow-Headers\n\nYou browser will make actual request if all of the requested headers are allowed.\n\n**Syntax:**\n\n```\nAccess-Control-Allow-Headers: \u003cheader-name\u003e, \u003cheader-name\u003e\nAccess-Control-Allow-Headers: *\n```\n\n**Example:**\n\n```\nAccess-Control-Allow-Headers: Accept, Content-Type\n```\n\nWildcard `*` tells browser that any header is allowed.\n\n#### 4.2.4. Access-Control-Max-Age\n\nTells how long the results of [pre-flight](#44-preflight-requests) can\nbe cached.\n\n```\nAccess-Control-Max-Age: \u003cdelta-seconds\u003e\n```\n\n\u003cdelta-seconds\u003e maximum no. of seconds the results can be cached.\n\nEach browser has max limit,\n- Firefox caps this at 24 hours(86400 seconds).\n- Chromium (prior to v76) caps at 10 minutes (600 seconds).\n- Chromium (starting in v76) caps at 2 hours (7200 seconds).\n- Chromium also specifies a default value of 5 seconds.\n- A value of -1 will disable caching, requiring a preflight OPTIONS check for all calls.\n\n#### 4.2.5. Vary\n\nVary header in general used for caching purpose to determine whether a cached response\ncan be used or it has to be re-cached based on the header value.\n\nIn CORS, lets say server allows multiple origins based on the requested `Origin` it will\nreturn particular URL in `Access-Control-Allow-Origin`.\n\n**Syntax:**\n\n```\nVary: \u003cheader-name\u003e\n```\n\n**Example:**\n\nLets say github.com allows both https://github.com as well as https://netflix.com to request for resources.\nConsider following scenarios,\n\n**Scenario 1:**\n\ncurl -X OPTIONS https://github.com/api/v1/gists/1\n\n```\nOrigin: https://github.com\nAccess-Control-Request-Method: GET\nAccess-Control-Request-Headers: Content-Type\n```\n\n```\nAccess-Control-Allow-Origin: https://github.com\nVary: Origin\nAccess-Control-Allow-Methods: GET, POST\nAccess-Control-Allow-Headers: Content-Type\nAccess-Control-Max-Age: 600\n```\n\nNow in this scenario, browser will cache this pre-flight request results for\n10 minutes.\n\n**Scenario 2:**\n\ncurl -X OPTIONS https://github.com/api/v1/gists/1\n\n```\nOrigin: https://netflix.com\nAccess-Control-Request-Method: GET\nAccess-Control-Request-Headers: Content-Type\n```\n\n```\nAccess-Control-Allow-Origin: https://netflix.com\nVary: Origin\nAccess-Control-Allow-Methods: GET, POST\nAccess-Control-Allow-Headers: Content-Type\nAccess-Control-Max-Age: 300\n```\n\nNow you could notice that `Access-Control-Allow-Origin` contains https://netflix.com,\nthis is example of how the response varies based on given `Origin`. So does the\nmax-age of this response which is cached for only 5 minutes unlike first scenario.\n\n### 4.3. Simple Requests\n\nSome request don't trigger [pre-flight](#44-preflight-requests) request for CORS.\nThese we call it as *simple requests*. It should meet the following conditions:\n\n- One of the allowed methods:\n  - GET\n  - POST\n  - DELETE\n\n- Other than headers which are automatically by the user agent (for example, `Connection` or\n`User-Agent`), the only headers which are allowed to manually set are [CORS-safelisted](#414-cors-safelisted)\nrequest headers and the following:\n\n  - `DPR`\n  - `Downlink`\n  - `Save-Data`\n  - `Viewport-Width`\n  - `Width`\n\n- No event listeners are registered on any `XMLHttpRequestUpload` object used in the\nrequest.\n\n- No `ReadableStream` object is used in the request.\n\n**Example:**\n\n*Request:*\n```\nGET /api/v1/public-data/ HTTP/1.1\nHost: bar.com\nUser-Agent: curl/7.69.1\nAccept: */*\nOrigin: https://foo.com\n```\n\n*Response Headers:*\n```\nHTTP/1.1 200 OK\nAccess-Control-Allow-Origin: *\nConnection: Keep-Alive\nTransfer-Encoding: chunked\nContent-Type: application/xml\n```\n\nWhen a request returns `*` in `Access-Control-Allow-Origin` header. It means a request\ncan be made from any `Host`. In this case, your browser won't make pre-flight request.\n\n### 4.4. Preflight Requests\n\nYour browser will make pre-flight request to determine if the actual request is safe\nto send.\n\n**Example:**\n\n```JavaScript\nconst xhr = new XMLHttpRequest();\nxhr.open('POST', 'https://bar.com/api/v1/post-here/');\nxhr.setRequestHeader('X-PINGOTHER', 'pingpong');\nxhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');\nxhr.onreadystatechange = handler;\nxhr.send(JSON.stringify({ \"email\": \"foo@bar.com\" }));\n```\n\n*Pre-flight Request:*\n\n```\nOPTIONS /api/v1/post-here/\nHost: bar.com\nUser-Agent: curl/7.69.1\nAccept: */*\nOrigin: https://foo.com\nAccess-Control-Request-Method: POST\nAccess-Control-Request-Headers: X-PINGOTHER, Content-Type\n\nHTTP/1.1 200 OK\nConnection: Keep-Alive\nContent-Type: application/json\nAccess-Control-Allow-Origin: https://foo.com\nAccess-Control-Allow-Methods: GET, POST\nAccess-Control-Allow-Headers: X-PINGOTHER, Content-Type, Accept\n```\n\n*Actual Request:*\n\n```\nPOST -d '{foo: bar}' /api/v1/post-here/\nHost: bar.com\nUser-Agent: curl/7.69.1\nAccept: */*\nOrigin: https://foo.com\nX-PINGOTHER: pingpong\nContent-Type: application/json;charset=UTF-8\n\nHTTP/1.1 200 OK\nConnection: Keep-Alive\nContent-Type: application/json\nAccess-Control-Allow-Origin: https://foo.com\n```\n\nWhen a non-standard header like `X-PINGOTHER` is set, your browser won't know if its\nsafe to make the request. In order to make sure its safe, your browser makes OPTIONS\nrequest with `Access-Control-Request-Headers` containing `X-PINGOTHER` and `Content-Type`. Upon validating with [response headers](#42-response-headers) of pre-flight\nrequest, your browser makes actual request.\n\n### 4.5. Request with credentials\n\nIn general, when you make a XHR request, cookies are not passed along with request.\nWhen there is need to pass cookies, you'll have to set a flag on the `XMLHttpRequest`\nobject.\n\n```JavaScript\nconst xhr = new XMLHttpRequest();\nconst url = 'http://bar.com/api/v1/credentialed-content/';\n\nfunction callOtherDomain() {\n  if (invocation) {\n    xhr.open('GET', url, true);\n    xhr.withCredentials = true;\n    xhr.onreadystatechange = handler;\n    xhr.send();\n  }\n}\n```\n\n*Request:*\n\n```\nPOST -d '{foo: bar}' /api/v1/post-here/\nHost: bar.com\nUser-Agent: curl/7.69.1\nAccept: */*\nOrigin: https://foo.com\nX-PINGOTHER: pingpong\nContent-Type: application/json;charset=UTF-8\nCookie: _session=NyV14oKXiS6HHopaf9nT\n```\n\nWhen a request is made `XMLHttpRequest` and `withCredentials` flag is set, your\nbrowser will pass down the `Cookie` in the request header.\n\n*Response:*\n```\nHTTP/1.1 200 OK\nConnection: Keep-Alive\nContent-Type: application/json\nAccess-Control-Allow-Origin: https://foo.com\nAccess-Control-Allow-Credentials: true\nCache-Control: no-cache\nPragma: no-cache\nSet-Cookie: _session=AjBSqxj4T7bSySNTWeEm; expires=Wed, 31-05-2020 00:00:00 GMT\n```\n\nWhen your browser notices `Access-Control-Allow-Credentials` set to true. It will\nrespect `Set-Cookie` header and sets the cookie.\n\n**Important:** \"\\*\" wildcard should not be set in the `Access-Control-Allow-Origin` like\nmentioned in the [Credentialed requests and wildcards](#52-credentialed-requests-and-wildcards) section.\n\n## 5. Advanced Topics\n\n### 5.1. Additional HTTP Response Headers\n\n#### 5.1.1. Access-Control-Expose-Headers\n\nWhen a credentials request is made by setting `withCredentials` flag, `Access-Control-Expose-Headers`\nhas to be set by server to let browser know which headers can be accessed.\n\nIn pre-cors world, by default response headers are not accessible by browser in the CORS request.\nSo it is made explicit so that browser will look for this header in order to read exposed headers.\nThis way CORS specification makes sure old browsers doesn't break.\n\n#### 5.1.2. Access-Control-Allow-Credentials\n\nThis is returned in the [pre-flight](#44-preflight-requests) requests. When your browser\nsees this, it can access `Set-Cookie` header. As we mentioned above, in normal XHR requests, your\nbrowser won't pass `Cookie` in request header as well as read `Set-Cookie` response header.\n\n**Syntax:**\n\n```\nAccess-Control-Allow-Credentials: true\n```\n\nYou can find example in the [Request with credentials](#45-request-with-credentials) section.\n\n### 5.2. Credentialed requests and wildcards\n\nWhen we say credentialed request it means cookies passed in the XHR request or set via the response\nheader `Set-Cookie`. When a XHR request is made with withCredentials flag, you're hoping to receive\nresponse along with cookies to be set. But, you cannot expect `Access-Control-Allow-Origin` to be \"\\*\"\nbecause that would mean any website can use these cookies. For this reason, your browser would fail\nthe request if it sees \"\\*\" in `Access-Control-Allow-Origin` response header.\n\n### 5.3. Preflighted requests and redirects\n\nWhen a pre-flight request responds with 301/302, some browsers may not support this\ncurrently. You might get errors like,\n\n`The request was redirected to 'https://example.com/foo', which is disallowed for cross-origin requests that require preflight`\n\n`Request requires preflight, which is disallowed to follow cross-origin redirect`\n\n**Note:** For workarounds check [Preflighted requests and redirects](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) docs by Mozilla.\n\n### 5.4. Third-party cookies\n\nBrowser has settings to reject all `third-party` cookies, when a user enables that. For example, if a request is\nmade from `https://foo.com` and server is at `https://bar.com`, your browser will not set cookies sent by `https://bar.com`.\n\n## 6. TODO\n\n- Add Screencasts\n\n## 7. References\n\n- [Cross-Origin Resource Sharing (CORS) by Mozilla](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvasuadari%2Flearn_cors","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvasuadari%2Flearn_cors","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvasuadari%2Flearn_cors/lists"}