[网站运维] 修复跨域(CORS)问题

发布于 2022-01-02  1119 次阅读


如果在你的网站上无法加载某个外站 URL 的资源,但是直接使用浏览器访问该 URL 却能够畅通无阻,那么你大概率遇到了 CORS 问题。这篇文章将向你讲解什么是 CORS 跨域问题以及如何对其进行修复。

你可以按 F12 打开开发者工具,选择网络 (Network) 选项卡,按 Ctrl + F5 刷新网页,然后找到出现问题的文件。如果显示为红色且错误码为 "CORS 错误" "CORS Policy" "CORS error" 等,即代表该文件是由于 CORS 问题而无法加载。

什么是CORS跨域问题

在 MDN 官方文档中,我们可以看到对 CORS 的定义解释:

跨源资源共享 (CORS)(或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它origin(域,协议和端口),这样浏览器可以访问加载这些资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的"预检"请求。在预检中,浏览器发送的头中标示有HTTP方法和真实请求中会用到的头。
跨源HTTP请求的一个例子:运行在 https://domain-a.com 的 JavaScript 代码使用 XMLHttpRequest 来发起一个到 https://domain-b.com/data.json 的请求。
出于安全性,浏览器限制脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest 和 Fetch API 遵循同源策略。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非响应报文包含了正确 CORS 响应头。

—— 跨源资源共享(CORS) - HTTP | MDN (mozilla.org)

如果你需要更详细的了解有关 CORS 的问题,那么强烈推荐你去阅读此文档。

接下来简单解释一下 CORS:

  • Q:什么是跨域?
  • A:两个 URL,只要符合协议不同 / 域名不同(包括主机名不同) / IP不同 / 端口号不同等,即视为两者互为跨域的。判断是否跨域是以实际访问的网站为基准的,如果网站调用的 URL 与浏览器地址栏显示的 URL 符合上述条件,即认为该请求是跨域的。
  • Q:为什么要区分跨域与不跨域?
  • A:脚本内发起跨域请求可能带来安全威胁。所以,浏览器会默认阻止部分跨站请求。
  • Q:在什么情况下,CORS 请求会被浏览器阻止?
  • A:由脚本发起的 CORS 请求,当被跨域的服务器没有允许时,浏览器不会加载该请求。但是,由文档 (HTML、CSS) 发起的 CORS 不会被阻止,即使该请求指向的是一个脚本文件。
  • Q:我如何令浏览器不阻止这些跨域请求?
  • A:只能修改被跨域 URL 的 HTTP 响应头文件。HTTP 响应头文件不能通过修改网页代码来改变 (完全不是一码事),只能修改服务端配置以修改响应头文件,从而向浏览器表明本站支持 CORS。这同时也证明了你对该服务器的拥有。
  • Q:服务器 / CDN 不属于我,我该怎么办?
  • A:如果你使用的是一个成熟的图床网站或者外链服务,那么你大可放心,大多数外链服务商 (包括 jsdelivr) 会默认为客户开放 CORS。或者,你也可以干脆迁移到不跨域的静态文件夹。

修复CORS跨域问题

方法一:在CDN端添加 HTTP 响应头以允许跨站访问(推荐)

以华为云CDN为例:打开CDN控制台 ==> 域名管理 ==> 选择你的外链服务器域名 ==> 高级配置 ==> HTTP header 配置 ==> 编辑,在其中设置 Access-Control-Allow-Origin 属性,最后刷新CDN和本地缓存。

操作图例

方法二:在服务器端修改配置文件以允许跨站访问(不推荐)

在你的外链服务器的配置文件里添加以下代码 (宝塔面板在 网站 ==> 选择你的外链站 ==> 配置文件,添加到适当位置),之后刷新CDN缓存 (如有):

  # 允许跨域
  location ~ .*\.(js|css|ttf|woff|woff2)?$    # 此处更改你允许的格式
  {
      add_header 'Access-Control-Allow-Origin' *;
  }
操作图例

需要注意的是,如果你有其他替代方案 (例如你拥有CDN),那么请优先选择该替代方案,因为在服务器端允许跨站可能会招致跨站攻击。如果你需要在服务器端修改,请一定要只允许你必要的格式,不要开放所有格式。

方法三:迁移到不跨域的静态文件夹(不推荐)

该方法即直接在不跨域的服务器中放置一个文件夹,并单独设置CDN规则。例如,直接在你的博客根目录新建一个 /static 文件夹,并在你的CDN ==> 缓存设置 中为其单独配置你的缓存方法。

这种解决方案比较万能,可谓是纯纯摆烂了,实现简单、虚拟主机也能做到,但是硬伤在于难以管理 (你总不能把整个 WordPress NTP到本地吧),也不利于动静态分离管理。所以,如果你的独立图床拥有CDN,建议先试试方法一。