{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/",
  "atlas": "https://www.pystone.net/?node=webgl-browser-http-cors-issues#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/webgl-browser-http-cors-issues.md",
  "context": "https://www.pystone.net/notes/webgl-browser-http-cors-issues.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:webgl-browser-http-cors-issues",
  "slug": "webgl-browser-http-cors-issues",
  "title": "WebGL浏览器前端HTTP请求与跨域资源共享问题",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [],
  "summary": "WebGL浏览器前端HTTP请求与跨域资源共享问题 相关概念",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "25efd3cf628caee0856564e90207496c94ca02a29cd3781d142a57d8796a22e0",
  "assets": [
    {
      "reference": "assets/image-20250625193435226.png",
      "url": "/media/46e8c57ed21e30421992.png",
      "mediaType": "image/png",
      "contentHash": "46e8c57ed21e304219924c84dc9ae5adadb57237686028d9e57931c3ad474aaa",
      "byteLength": 179798,
      "width": 3068,
      "height": 846
    }
  ],
  "headings": [
    {
      "depth": 1,
      "text": "WebGL浏览器前端HTTP请求与跨域资源共享问题",
      "anchor": "webgl浏览器前端http请求与跨域资源共享问题",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#webgl%E6%B5%8F%E8%A7%88%E5%99%A8%E5%89%8D%E7%AB%AFhttp%E8%AF%B7%E6%B1%82%E4%B8%8E%E8%B7%A8%E5%9F%9F%E8%B5%84%E6%BA%90%E5%85%B1%E4%BA%AB%E9%97%AE%E9%A2%98"
    },
    {
      "depth": 2,
      "text": "相关概念",
      "anchor": "相关概念",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#%E7%9B%B8%E5%85%B3%E6%A6%82%E5%BF%B5"
    },
    {
      "depth": 3,
      "text": "Same-Origin Security Policy（SOP）",
      "anchor": "same-origin-security-policysop",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#same-origin-security-policysop"
    },
    {
      "depth": 3,
      "text": "在 SOP 之上如何“合法跨域”——CORS 基本原则",
      "anchor": "在-sop-之上如何合法跨域cors-基本原则",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#%E5%9C%A8-sop-%E4%B9%8B%E4%B8%8A%E5%A6%82%E4%BD%95%E5%90%88%E6%B3%95%E8%B7%A8%E5%9F%9Fcors-%E5%9F%BA%E6%9C%AC%E5%8E%9F%E5%88%99"
    },
    {
      "depth": 3,
      "text": "核心响应头速查表",
      "anchor": "核心响应头速查表",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#%E6%A0%B8%E5%BF%83%E5%93%8D%E5%BA%94%E5%A4%B4%E9%80%9F%E6%9F%A5%E8%A1%A8"
    },
    {
      "depth": 3,
      "text": "典型配置方案",
      "anchor": "典型配置方案",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#%E5%85%B8%E5%9E%8B%E9%85%8D%E7%BD%AE%E6%96%B9%E6%A1%88"
    },
    {
      "depth": 4,
      "text": "阿里云 OSS Bucket（控制台示例）",
      "anchor": "阿里云-oss-bucket控制台示例",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#%E9%98%BF%E9%87%8C%E4%BA%91-oss-bucket%E6%8E%A7%E5%88%B6%E5%8F%B0%E7%A4%BA%E4%BE%8B"
    },
    {
      "depth": 4,
      "text": "Nginx 反向代理",
      "anchor": "nginx-反向代理",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#nginx-%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86"
    },
    {
      "depth": 4,
      "text": "Node.js / Express",
      "anchor": "nodejs-express",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#nodejs-express"
    },
    {
      "depth": 2,
      "text": "浏览器与小程序平台的差异",
      "anchor": "浏览器与小程序平台的差异",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%8E%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%B9%B3%E5%8F%B0%E7%9A%84%E5%B7%AE%E5%BC%82"
    },
    {
      "depth": 3,
      "text": "小程序平台",
      "anchor": "小程序平台",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%B9%B3%E5%8F%B0"
    },
    {
      "depth": 3,
      "text": "浏览器平台",
      "anchor": "浏览器平台",
      "citation": "https://www.pystone.net/notes/webgl-browser-http-cors-issues/#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%B9%B3%E5%8F%B0"
    }
  ],
  "claims": [],
  "outgoing": [],
  "incoming": [
    {
      "id": "note:game-graphics-and-runtime",
      "title": "游戏图形与运行时",
      "url": "https://www.pystone.net/notes/game-graphics-and-runtime/",
      "atlas": "https://www.pystone.net/?node=game-graphics-and-runtime#knowledge-atlas",
      "label": "游戏图形与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "WebGL中的“WebGL浏览器前端HTTP请求与跨域资源共享问题”导航项",
      "citation": "https://www.pystone.net/notes/game-graphics-and-runtime/#webgl"
    }
  ],
  "contentMarkdown": "# WebGL浏览器前端HTTP请求与跨域资源共享问题\n## 相关概念\n\n### Same-Origin Security Policy（SOP）\n**同源策略 (Same-Origin Policy, SOP)**\n\n| 要点         | 说明                                                                                                   |\n| ---------- | ---------------------------------------------------------------------------------------------------- |\n| **定义**     | 浏览器只允许脚本访问与其页面 *协议 scheme*、*主机 host*、*端口 port* 完全一致的资源。默认禁止跨域请求（协议/域名/端口任一不同即跨域）                     |\n| **意义**     | 把每一个 *Origin* 隔离成沙箱，阻止恶意站点窃读或修改另一站点的受信数据（Cookies、表单、银行信息等）。([portswigger.net][2])                    |\n| **若无 SOP** | 任何页面都可在用户登录状态下偷偷：• 读取邮箱 / 网银 API 返回内容 → 隐私泄露• 执行受信操作（转账、删数据）→ CSRF / 账户被盗• 探测内网资产 → 越权扫描 |\n\n**作用范围**：DOM、`localStorage`、`IndexedDB`、`Cookies`、AJAX/Fetch 等。\n\n**XMLHttpRequest / Fetch API 的 CORS 行为**\n* 默认 `mode: \"cors\"` 会触发浏览器的同源检查并自动处理预检。\n* 若使用 `withCredentials = true` 或 `credentials: \"include\"`，服务器必须返回匹配的 `Access-Control-Allow-Credentials: true`，且 `Access-Control-Allow-Origin` 不能是 `*`。\n\n### 在 SOP 之上如何“合法跨域”——CORS 基本原则\n**CORS** (Cross-Origin Resource Sharing) is a system, consisting of transmitting [HTTP headers](https://developer.mozilla.org/en-US/docs/Glossary/HTTP_header), that determines whether browsers block frontend JavaScript code from accessing responses for cross-origin requests.\n\n若页面需访问位于不同源（协议、主机或端口不完全一致）的资源，则必须由**目标服务端**在 HTTP 响应中明确回送 **CORS（Cross-Origin Resource Sharing）** 相关响应头，授权当前页面的 Origin 进行跨域交互。否则，即使网络链路连通，浏览器也会在收到响应前强制拦截并抛出 “No ‘Access-Control-Allow-Origin’ header” 错误。\n\n| 步骤                        | 目的                                                                     |\n| ------------------------- | ---------------------------------------------------------------------- |\n| **① 浏览器附带 `Origin:` 请求头** | 告诉目标服务器：“谁在跨域访问你”。                                                     |\n| **② 目标服务器返回 CORS 响应头**    | 显式声明哪些外域可访问、可用哪些方法、是否允许携带凭证。                                           |\n| **③ 浏览器校验**               | 只有当 `Access-Control-Allow-Origin` 等字段与请求匹配时，才把响应交给前端脚本；否则网络层收到但被浏览器丢弃。 |\n\n> 结论：**配置点始终在目标域（后端 / OSS / CDN），而不是在发起请求的前端。**\n\n---\n\n### 核心响应头速查表\n\n| Header                             | 作用               | 示例值                                |\n| ---------------------------------- | ---------------- | ---------------------------------- |\n| `Access-Control-Allow-Origin`      | 允许的 Origin       | `https://yourgame.com` 或 `*`       |\n| `Access-Control-Allow-Methods`     | 允许的 HTTP 方法      | `GET,POST,PUT,OPTIONS`             |\n| `Access-Control-Allow-Headers`     | 允许的自定义请求头        | `Content-Type,Authorization` 或 `*` |\n| `Access-Control-Allow-Credentials` | 是否允许 Cookie / 令牌 | `true`（配合具体 Origin，不能是 `*`）        |\n| `Access-Control-Max-Age`           | 预检结果缓存秒数         | `3600`                             |\n| `Access-Control-Expose-Headers`    | 前端可读的额外响应头       | `ETag,x-oss-request-id`            |\n\n---\n\n### 典型配置方案\n\n#### 阿里云 OSS Bucket（控制台示例）\n\n在使用 **阿里云 OSS** 直传文件的场景中，OSS 服务即为“目标域”，需启用 Bucket-级别的 CORS 规则。推荐采用**最小可用授权**原则，仅开放实际需要的域名、方法与头部。\n\n| 配置项               | 建议设置                                                                                  | 说明                                                        |\n| ----------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------- |\n| **AllowedOrigin** | `https://<业务域名>``http://localhost:8080`（调试）                                           | 必填；多域名可多行列出，必须带协议与端口                                      |\n| **AllowedMethod** | `POST` `PUT` `OPTIONS`                                                                | 若仅上传，可按需保留 `POST/PUT`；`OPTIONS` 为浏览器预检必选                  |\n| **AllowedHeader** | `*` 或列出 `Content-Type,Authorization`                                                  | 建议开发期先放宽为 `*`，上线前收紧                                       |\n| **ExposeHeader**  | `ETag,x-oss-request-id`                                                               | 供前端读取响应头                                                  |\n| **MaxAgeSeconds** | `3600`                                                                                | 预检结果在浏览器侧缓存时长                                             |\n| **凭证请求**          | **需开启** `Access-Control-Allow-Credentials:true`（由 OSS 自动返回）并确保 `AllowedOrigin` 不为 `*` | 仅当前端使用 `withCredentials:true / credentials:'include'` 时适用 |\n\n**控制台快速配置步骤**\n1. 登录 OSS 控制台 → 选择 Bucket。\n2. 进入 **数据安全 › 跨域设置**，点击 **创建规则**。\n3. 按上表填写，确认保存后即时生效。\n\n**CLI / 自动化示例（ossutil）**\n\n```html\n<CORSConfiguration>\n  <CORSRule>\n    <AllowedOrigin>http://localhost:8080</AllowedOrigin>\n    <AllowedOrigin>https://yourgame.com</AllowedOrigin>\n    <AllowedMethod>POST</AllowedMethod>\n    <AllowedMethod>PUT</AllowedMethod>\n    <AllowedMethod>OPTIONS</AllowedMethod>\n    <AllowedHeader>*</AllowedHeader>\n    <ExposeHeader>ETag</ExposeHeader>\n    <ExposeHeader>x-oss-request-id</ExposeHeader>\n    <MaxAgeSeconds>3600</MaxAgeSeconds>\n  </CORSRule>\n</CORSConfiguration>\n```\n\n推送命令：`ossutil cors --method put oss://your-bucket cors.xml`。([help.aliyun.com][3])\n\n> **带凭证上传**：浏览器需 `withCredentials=true`，并确保 `AllowedOrigin` 为具体域名，OSS 会自动加 `Access-Control-Allow-Credentials:true`。\n\n---\n\n#### Nginx 反向代理\n\n```nginx\nlocation /api/ {\n    add_header Access-Control-Allow-Origin https://yourgame.com;\n    add_header Access-Control-Allow-Methods \"GET, POST, PUT, OPTIONS\";\n    add_header Access-Control-Allow-Headers \"*\";\n    add_header Access-Control-Allow-Credentials true;\n    if ($request_method = OPTIONS) { return 204; }\n}\n```\n\n#### Node.js / Express\n\n```js\nconst cors = require('cors');\napp.use(cors({\n  origin: ['https://yourgame.com', 'http://localhost:8080'],\n  methods: ['GET', 'POST', 'PUT'],\n  allowedHeaders: ['Content-Type', 'Authorization'],\n  credentials: true,\n  maxAge: 3600\n}));\n```\n\n\n注意:\n* 如果服务器随意返回 `Access-Control-Allow-Origin: *` 并允许凭证，会暴露会话给任何站点，存在 CSRF/数据泄露风险。\n* 因此服务器端 CORS 配置应尽量**最小化许可范围**。\n\n## 浏览器与小程序平台的差异\n\n| 特性               | 微信小程序运行时                                                                     | 浏览器 (WebGL/H5)                             |\n| ---------------- | ---------------------------------------------------------------------------- | ------------------------------------------ |\n| 网络 API           | `wx.request()` ➜ **原生 TCP/HTTPS**，内部实现近似 iOS/Android 的 `NSURLSession/OkHttp` | `XMLHttpRequest` / `fetch` ➜ **浏览器网络线程**   |\n| 是否发送 `Origin:` 头 | 不发送（所以服务器根本不知道这是谁跨来的域）                                                       | 必须发送；浏览器用它比对 `Access-Control-Allow-Origin` |\n| 是否受同源策略          | **不受** ——「跨域」概念对它不存在                                                         | **受** —— 默认禁止跨源，除非服务器通过 CORS 放行            |\n| 客户端白名单机制         | 「request 合法域名」写在微信后台，微信*本地*先检查                                               | 无                                          |\n| 服务器需否配置 CORS     | **不用**                                                                       | **必须**（否则浏览器在收到响应前就拦截）                     |\n\n![](assets/image-20250625193435226.png)\n\n\n### 小程序平台\n微信小游戏运行在微信的环境中，其网络请求是通过微信的客户端（即微信App）发起的，而不是直接由浏览器发起。微信小游戏使用`wx.request` API发送请求，这个API有以下特点：\n- **非浏览器环境**：微信小游戏的JavaScript运行环境是微信自己提供的，不是浏览器，因此不会受到浏览器的同源策略（Same-Origin Policy）限制。\n- **域名白名单（客户端拦截机制）**：微信小游戏要求开发者在小程序或小游戏的后台配置请求的合法域名（即request合法域名）。微信客户端在发起请求时会检查目标域名是否在合法域名列表中，如果不在，则请求会被微信客户端阻止。这个机制是微信自己实现的，与浏览器的CORS机制无关。\n- **不涉及CORS**：由于请求是由微信客户端（原生应用）发起的，而不是浏览器，所以不会触发浏览器的CORS检查。因此，即使阿里云OSS没有配置CORS规则，只要域名在微信的合法域名列表中，请求就能成功。\n\n\n### 浏览器平台\n**遵循W3C规范**：浏览器严格执行 [CORS标准](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)\n在浏览器环境中，当你的Web应用通过`XMLHttpRequest`或`fetch`向不同的域（如阿里云OSS的域名）发起请求时，浏览器会强制执行同源策略，并检查CORS响应头。\n\n- **CORS机制**：浏览器会首先发送一个预检请求（OPTIONS请求）到目标服务器（阿里云OSS），询问是否允许来自`http://localhost:8080`的请求。服务器必须返回`Access-Control-Allow-Origin`响应头，并且该头部的值需要包含当前域（或`*`），浏览器才会允许实际的请求。\n\n- **OSS配置**：阿里云OSS作为资源服务器，必须配置CORS规则以响应预检请求，即返回适当的`Access-Control-Allow-Origin`等头部。如果没有配置，浏览器就会报错，如你遇到的错误。\n"
}
