使用fetch获取图片触发CORS策略拦截问题求助
项目情况
- 开发一款允许用户创建项目并上传最多5张图片的Web应用,采用PHP搭建API
- 已在
index.php中配置CORS规则:
// 指定允许跨域请求的域名 header('Access-Control-Allow-Origin: *'); // 指定允许的请求方法 header('Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS'); // 允许携带的请求头 header('Access-Control-Allow-Headers: X-Requested-With,Authorization,Content-Type'); // 设置缓存有效期1天以提升速度 header('Access-Control-Max-Age: 86400'); // OPTIONS请求直接返回,避免加载完整页面 if (strtolower($_SERVER['REQUEST_METHOD']) == 'options') { exit(); }
- 图片存储在
public/img目录,通过php -S localhost:3000 -t public/启动PHP内置服务器 - 所有API接口路由返回JSON响应,Web应用能正常接收;使用
<img src="http://localhost:3000/img/image.jpg">可正常显示图片,直接在浏览器或Postman访问图片地址也无问题
问题现象
使用以下JavaScript代码fetch图片时触发CORS错误:
const image = await fetch(BASE_URL + imagePath); // 实际请求地址:http://localhost:3000/img/image.jpg const imageBlob= await image.blob();
错误信息:
project.html:1 Access to fetch at 'http://localhost:3000/img/image.jpg' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
排查确认:图片请求未经过index.php
原因分析
使用php -S localhost:3000 -t public/启动服务器时,PHP内置服务器会直接返回public目录下的静态文件(如图片),不会经过index.php。因此index.php中配置的CORS规则仅对通过该文件转发的API请求生效,静态图片请求由服务器直接返回,未携带CORS响应头,导致跨域fetch时触发浏览器的同源策略校验错误。
而<img>标签加载图片属于资源嵌入场景,浏览器对这类请求的CORS校验有例外规则,因此能正常显示;但fetch请求属于跨域数据请求,浏览器会严格校验响应头,缺少Access-Control-Allow-Origin就会报错。
解决办法
方法1:修改PHP内置服务器路由,让所有请求经过统一入口
启动服务器时添加路由规则,将所有请求转发到自定义路由文件处理:
php -S localhost:3000 -t public/ router.php
在项目根目录创建router.php,内容如下:
<?php // 先处理CORS规则 header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS'); header('Access-Control-Allow-Headers: X-Requested-With,Authorization,Content-Type'); header('Access-Control-Max-Age: 86400'); if (strtolower($_SERVER['REQUEST_METHOD']) == 'options') { exit(); } // 处理静态文件请求 $requestUri = $_SERVER['REQUEST_URI']; $filePath = __DIR__ . '/public' . $requestUri; // 如果是存在的静态文件,返回文件内容 if (file_exists($filePath) && is_file($filePath)) { // 设置对应文件的Content-Type $mime = mime_content_type($filePath); header("Content-Type: $mime"); readfile($filePath); exit(); } // 其他请求交给index.php处理 require_once __DIR__ . '/public/index.php';
这样所有请求(包括静态图片)都会经过router.php,自动携带CORS响应头,fetch请求即可正常处理。
方法2:在生产环境Web服务器中配置静态文件CORS头
如果后续使用Nginx/Apache部署,可直接在服务器配置中为img目录添加CORS规则:
- Nginx配置示例:在
server块中添加
location /img/ { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET,OPTIONS; }
- Apache配置示例:在
public/.htaccess中添加
<Directory "img/"> Header set Access-Control-Allow-Origin "*" Header set Access-Control-Allow-Methods "GET,OPTIONS" </Directory>
方法3:临时使用no-cors模式(不推荐用于需处理Blob内容的场景)
如果仅需获取Blob但无需读取内容,可设置fetch的mode: 'no-cors',但此时返回的是不透明响应,无法解析Blob内容,仅能用于缓存或嵌入页面等场景:
const image = await fetch(BASE_URL + imagePath, { mode: 'no-cors' }); const imageBlob = await image.blob(); // 注意:该Blob无法正常读取内容,仅可用于如URL.createObjectURL(imageBlob)嵌入页面的场景
内容的提问来源于stack exchange,提问作者Lucas da Paz

