为什么本地文件用 fetch 请求 JSON 会报 CORS 错误?
我最近在本地开发一个静态页面,想用 fetch('data.json') 加载同目录下的 JSON 文件,结果浏览器控制台报错:「Access to fetch at ‘file:///…/data.json’ from origin ‘null’ has been blocked by CORS policy」。明明是本地文件,又没跨域,怎么会触发 CORS?
我已经试过把文件放到 VS Code 的 Live Server 里跑,这样就能正常加载,但直接双击 HTML 文件打开就不行。是不是因为 file:// 协议的问题?有没有办法在不启动服务器的情况下解决?
浏览器出于安全考虑,默认禁止通过 file:// 协议发起的跨源请求,即使是在本地文件系统里。当你直接双击打开 HTML 文件时,浏览器会把当前 origin 识别为 'null',而 file:// 协议下的 JSON 文件也被视为不同源,所以就触发了 CORS 限制。
解决办法有几种,我按推荐顺序说:
1. 最佳方案:使用本地开发服务器(推荐)
用 VS Code 的 Live Server 或者任何轻量级服务器(比如 http-server)都可以。因为这种情况下 origin 变成了 localhost,同源策略就正常了。
2. 如果非要直接打开文件:
对于 Chrome/Edge 可以这样启动(注意这会降低安全性):
或者用 Firefox 的 about:config 里设置:
security.fileuri.strict_origin_policy改为 false3. 另一个变通方法是直接把 JSON 数据内联到 JS 文件里:
4. 如果只是临时测试,可以尝试用
fetch()的 catch 捕获错误,然后用 Node.js 的 fs 模块读取文件(仅限测试环境)。原理上,现代浏览器对 file:// 协议的限制越来越严格,这是为了防止本地文件被恶意网页读取。我建议还是用开发服务器,这才是正经的开发方式。毕竟你后面要部署到 web 服务器上时,迟早要面对这些问题的。
另外注意,用第二种方法修改浏览器设置后,记得测试完就改回来,不然会有安全隐患。
file://协议搞的鬼。问题的根源在于浏览器的安全策略。当你直接双击 HTML 文件打开时,地址栏是
file:///开头的,这时候页面的 origin 被浏览器定义为字符串"null"。注意不是空字符串,而是字符串 "null"。当你用 fetch 请求另一个本地文件时,浏览器会认为这是从一个 origin 为 "null" 的源去访问另一个源,即使它们在同一个文件夹里。Chrome 和其他现代浏览器出于安全考虑,禁止
file://协议的页面发起任何 AJAX 或 fetch 请求。这个设计是为了防止恶意网页读取你本地的文件系统,想象一下如果网页能随意读取你电脑上的文件,那得多危险。Live Server 能跑通是因为它启动了一个本地 HTTP 服务器,你的页面变成了
http://127.0.0.1:5500,请求 JSON 文件时是同源的,自然就没有 CORS 问题了。如果你非要不用服务器,有几种方案,但都有局限。
方案一,给 Chrome 加启动参数。关闭所有 Chrome 窗口,然后用命令行启动:
这个方法能跑,但我不建议用。首先每次都要手动启动很麻烦,其次这会关闭浏览器的安全策略,你日常上网千万别用这个模式的浏览器。
方案二,把 JSON 数据直接内嵌到 JS 文件里。如果你的数据是静态的,不经常变动,直接写成一个变量:
然后在 HTML 里用
引入,直接用localData这个变量就行。简单粗暴,但失去了 JSON 文件的灵活性。方案三,如果你只是想快速测试,可以用 Python 一行命令起个服务器。前提是你电脑装了 Python:
或者用 Node.js 的
http-server包,全局安装后直接运行就行。说到底,本地开发起个服务器是正道。VS Code 的 Live Server 插件也好,Python 的简易服务器也好,都是几秒钟的事。浏览器的安全策略不是来跟你作对的,是真的在保护用户。我之前为了调试一个本地存储的问题折腾了半天,最后发现就是
file://协议下 localStorage 的行为和http://也不一样,坑多了去了。总结一下,没有服务器的情况下完美解决这个问题的方案基本没有,要么接受安全风险改浏览器参数,要么把数据内嵌,要么老老实实起个本地服务器。建议还是用 Live Server 或者其他本地服务器方案,省心。