{"id":17640923,"url":"https://github.com/beace/cors-preflight","last_synced_at":"2025-03-30T05:24:19.525Z","repository":{"id":98535619,"uuid":"171234236","full_name":"Beace/CORS-preflight","owner":"Beace","description":"CORS-preflight demo","archived":false,"fork":false,"pushed_at":"2019-02-18T07:31:19.000Z","size":4,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-05T07:31:19.610Z","etag":null,"topics":["cors-preflight","cors-request"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/Beace.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":"2019-02-18T07:18:32.000Z","updated_at":"2019-04-12T08:46:27.000Z","dependencies_parsed_at":"2023-05-29T09:15:22.482Z","dependency_job_id":null,"html_url":"https://github.com/Beace/CORS-preflight","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/Beace%2FCORS-preflight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Beace%2FCORS-preflight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Beace%2FCORS-preflight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Beace%2FCORS-preflight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Beace","download_url":"https://codeload.github.com/Beace/CORS-preflight/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246279916,"owners_count":20752036,"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":["cors-preflight","cors-request"],"created_at":"2024-10-23T06:24:46.553Z","updated_at":"2025-03-30T05:24:19.504Z","avatar_url":"https://github.com/Beace.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CORS-preflight fetch(request)\n跨域预检请求，术语为 CORS-preflight fetch 或 CORS-preflight requst。**浏览器**默认有不得跨域请求资源的限制，因此服务端往往在 response header 中加入相应的允许跨域请求的请求头，允许前端对 API 进行跨域请求。浏览器识别服务器是否允许跨域请求资源，是通过预检来完成。这篇文章，主要是来记录跨域的预检请求。\n\n## CORS-safelisted method\n在服务端设置 `Access-Control-Allow-Origin` 为相应域名后，浏览器就允许按照下面这三中方法来跨域请求资源了。\n\n- GET\n- HEAD\n- POST\n\n也就是说，在服务端设置 `Access-Control-Allow-Origin`，资源就允许通过以上三种方法进行跨域访问。\n\n以下示例。\n\n```bash\ntouch server.js server2.js index.html\n```\n\n在 `server.js` 中起一个基础的服务，为 `3000` 端口，作为服务端来提供资源\n\n```javascript\nconst http = require('http');\n\nconst server = http.createServer((req, res) =\u003e {\n  res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3001')\n  res.end('123');\n});\n\nserver.listen(3000, () =\u003e {\n  console.log('server is listening on port: 3000...');\n});\n```\n\n在 `server2.js` 中再起一个基础的服务，为 `3001` 端口，作为客户端渲染 `HTML`，并且请求 `3000` 端口资源。\n\n```javascript\nconst http = require('http');\nconst fs = require('fs');\n\nconst server = http.createServer((req, res) =\u003e {\n  const html = fs.readFileSync('./index.html', 'utf-8');\n  res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3001')\n  res.end(html);\n});\n\nserver.listen(3001, () =\u003e {\n  console.log('server is listening on port: 3001...');\n});\n```\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"UTF-8\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /\u003e\n    \u003cmeta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" /\u003e\n    \u003ctitle\u003eCORS-preflight\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1\u003eCORS-preflight\u003c/h1\u003e\n    \u003cscript\u003e\n      fetch('http://localhost:3000', {\n        method: 'POST', // OR GET HEAD\n      })\n        .then(res =\u003e res.text())\n        .then(data =\u003e console.log(data));\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n启动服务后进入 http://localhost:3001 可以看到，可以正常访问到 `3000` 端口的资源。\n\n\n![](https://imgs.beacelee.com/2019/cors-preflight/1.png)\n\n可以尝试将方法改为 `GET` 和 `HEAD`。\n\n浏览器认为如果不是以上的安全请求方法，都会在实际请求 API 时会发出 method 为 `OPTIONS` 的预检请求。并且，对请求的方法有如下限制，以下称为 CORS 安全方法。\n\n我们来修改下 `index.html` 中 fetch API 中的 method，这里改为 `PUT`.\n\n```javascript\nfetch('http://localhost:3000', {\n\tmethod: 'POST',\n})\n\t.then(res =\u003e res.text())\n\t.then(data =\u003e console.log(data));\n```\n\n刷新页面，可以看到浏览器抛出了错误。\n\n![](https://imgs.beacelee.com/2019/cors-preflight/2.png)\n\n并且 devtool 中也可以看出事先发出了 `OPTIONS` 请求，虽然在 response 中可以看到数据，但是在代码中，获取不到实际的数据。\n\n![](https://imgs.beacelee.com/2019/cors-preflight/3.png)\n\n\n浏览器明确地提示，`Method PUT` 是不被允许的。或者更加明确地说，浏览器的预检（preflight）请求中没有接受到服务器返回的 `Access-Control-Allow-Methods` 中包含该方法。\n\n修改 `server.js` 中的代码，在增加 header\n\n```javascript\nres.setHeader('Access-Control-Allow-Methods', 'PUT')\n```\n\n重启服务器后可以看到，有两个请求发送，一个还是浏览器的预检请求，另外，则是 `PUT`,并成功返回了数据。\n\n\n![](https://imgs.beacelee.com/2019/cors-preflight/4.png)\n\n除了`PUT`之外，通常来讲，还有 `DELETE` 等方法，在实际开发中，会一并加上。\n\n## CORS-safelisted request-header\n除了 method 之外，CORS 还对请求头有一些限制。其中除以下请求头之外，其他的都会被block掉。\n\n```\naccept\naccept-language\nconent-languate\ncontent-type\n```\n\n### Content-Type\n对于 `content-type`  而言，也有以下限制。只允许以下三种。\n\n- text/plain\n- multipart/form-data\n- application/x-www-form-urlencoded\n\n我们来修改 `index.html` ，在 fetch 的 headers 中加入 content-type\n\n```javascript\nfetch('http://localhost:3000', {\n\tmethod: 'PUT',\n\theaders: {\n\t\t'Content-Type': 'application/json',\n\t},\n\tbody: JSON.stringify({\n\t\thello: 'world'\n\t})\n})\n```\n\n刷新浏览器，可以看到浏览器明确指出请求头 `content-type`  是不被允许的。\n\n![](https://imgs.beacelee.com/2019/cors-preflight/5.png)\n\n我们来修改下 `server.js` ，增加 `content-type` 为 `application/json` 的请求头。\n\n```javascript\nres.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n```\n\n重启服务，刷新浏览器，可以看到 `Content-Type` 为 `application/json`的可以成功接收到返回。\n\n![](https://imgs.beacelee.com/2019/cors-preflight/6.png)\n\n### Custom-header\n如果是自定义的 header 呢，比如我们需要传递给服务器一个 token 来标识用户身份，修改 index.html\n\n```javascript\nfetch('http://localhost:3000', {\n\tmethod: 'PUT',\n\theaders: {\n\t\t'Content-Type': 'application/json',\n\t\t'X-Auth-Token': 'auth-token',\n\t},\n\tbody: JSON.stringify({\n\t\thello: 'world'\n\t})\n})\n```\n\n刷新浏览器，发现，会有上面同样的错误：不允许 `x-auth-token`。同样，修改 server.js 在，setHeader中追加\n\n```javascript\nres.setHeader('Access-Control-Allow-Headers', 'Content-Type,X-Auth-Token');\n```\n\n如果以上设置太麻烦，可以通过`*`来设置。\n\n```javascript\nres.setHeader('Access-Control-Allow-Headers', '*');\n```\n\n## Access-Control-Max-Age\n如此重复地去发送预检请求，并不是一个好的方式。可以通过设置过期时间的方式，在保证安全的情况下在固定时间避免重复地检查。\n\n修改 `server.js` ,在 header 中设置 `Access-Control-Max-Age`\n\n```javascript\nres.setHeader('Access-Control-Max-Age', 10);\n```\n\n我们约定，在10s内不再检查。重启服务，刷新浏览器。\n\n![](https://imgs.beacelee.com/2019/cors-preflight/7.png)\n\n可以看到，第一次还是进行了预检请求，10s内刷新浏览器，直接请求了 API，10s后再次刷新，预检之后发起了请求。\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeace%2Fcors-preflight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeace%2Fcors-preflight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeace%2Fcors-preflight/lists"}