解决 React 中 img 加载图片报 403 Forbidden 错误
问题描述
在 React 项目中,使用 <img>
标签加载某些网页上的图片时,可能会遇到 403 Forbidden
错误。奇怪的是,如果直接在浏览器地址栏中打开该图片的 URL,图片却能正常加载。
例如,在下面的 React 代码中,尝试加载 object
(图片 URL) 时可能会失败:
import React from 'react'
import { Carousel } from 'some-ui-library' // 假设使用了某个轮播组件
import styles from './MyComponent.module.css'
class MyComponent extends React.Component {
bannerAfterChange = (current) => {
console.log(current)
}
render() {
// 假设 this.props.homemodel.bannerlist 是一个包含图片 URL 的数组
if (!this.props.homemodel || !this.props.homemodel.bannerlist) {
return <div>Loading...</div>
}
return (
<Carousel ref="banner" autoplay={false} dots={false} afterChange={this.bannerAfterChange.bind(this)}>
{
this.props.homemodel.bannerlist.map((object, index) => {
return (
<div className={styles.slide} key={index}>
{/* 直接加载图片可能会报 403 */}
<img className={styles.banner_image} src={object} alt={`Banner ${index + 1}`} />
</div>
)
})
}
</Carousel>
)
}
}
export default MyComponent
原因分析
这个问题通常与 HTTP 的 Referrer Policy(来源策略)有关。
浏览器在发起请求时,通常会带上 Referer
HTTP 头,告诉服务器请求是从哪个页面发起的。某些图片服务器为了防止图片被盗链,会检查 Referer
头。如果 Referer
指向一个不允许的来源(比如你的本地开发环境 localhost
或其他非授权域名),或者请求没有 Referer
头,服务器就可能返回 403 Forbidden
错误。
当你直接在浏览器地址栏打开图片 URL 时,没有来源页面,浏览器可能不发送 Referer
头,或者发送一个服务器允许的 Referer
,因此图片能正常加载。
解决方案
核心思路是控制浏览器发送 Referer
头的行为。
方案一:在 <img>
标签上设置 referrerPolicy
(推荐)
这是最推荐的方式,因为它作用范围精确,符合组件化开发的思想。
修改 <img>
标签,添加 referrerPolicy='no-referrer'
属性:
<img
className={styles.banner_image}
src={object}
alt={`Banner ${index + 1}`}
referrerPolicy="no-referrer"
/>
referrerPolicy='no-referrer'
告诉浏览器在请求这张图片时,不要发送 Referer
头。这样,服务器就不会因为 Referer
不符合要求而拒绝请求。
优点:
- 精确控制: 只影响当前
<img>
标签,不会影响页面上的其他请求。 - 组件封装: 逻辑封装在组件内部,符合组件化开发思想,易于维护。
缺点:
- 重复设置: 如果页面上有多个需要此策略的图片,需要在每个
<img>
标签上单独设置。
方案二:在 HTML <head>
中设置全局 <meta>
标签
你也可以在项目的 public/index.html
(或其他入口 HTML 文件) 的 <head>
部分添加一个 <meta>
标签,来设置全局的 Referrer Policy:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
{/* 在这里添加 meta 标签 */}
<meta name="referrer" content="no-referrer">
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
这种方式会影响该页面发起的所有请求,除非某个请求(例如通过 fetch
或 <img>
的 referrerPolicy
属性)单独覆盖了策略。
优点:
- 全局生效: 一次设置,对整个页面的所有请求(默认)生效,省去对单个元素重复设置的麻烦。
缺点:
- 影响范围广: 可能会影响到页面上其他不需要此策略的请求,需要谨慎评估全局影响。
- 不够精确: 控制粒度较粗,不如方案一灵活。
注意事项
使用 no-referrer 策略会有一些潜在影响:
- 分析工具: 如果你使用了依赖 Referer 头的分析工具,它们可能无法正确追踪流量来源
- 第三方服务: 某些第三方服务可能依赖 Referer 来验证请求合法性
- 特定功能: 少数网站功能可能依赖 Referer 信息
Referrer Policy 详解
referrerPolicy
属性或 <meta name="referrer">
的 content
属性可以接受多个值,用于更精细地控制 Referer
头的发送策略:
no-referrer
: 不发送Referer
头。no-referrer-when-downgrade
(默认值): 当从 HTTPS 页面请求 HTTP 资源时不发送Referer
头,其他情况发送完整的 URL。origin
: 只发送来源页面的源(协议、域名、端口),不包含路径和查询参数。origin-when-cross-origin
: 同源请求时发送完整的 URL,跨域请求时只发送源。same-origin
: 只对同源请求发送Referer
头,跨域请求不发送。strict-origin
: 始终只发送源。但从 HTTPS 页面请求 HTTP 资源时不发送。strict-origin-when-cross-origin
: 同源请求发送完整 URL;跨域请求时,如果安全级别不变(HTTPS->HTTPS)则发送源,如果降级(HTTPS->HTTP)则不发送。unsafe-url
: 始终发送完整的 URL(包括路径和查询参数),即使是从 HTTPS 页面请求 HTTP 资源。(不推荐,可能泄露敏感信息)
对于解决图片 403 问题,no-referrer
通常是最简单直接有效的策略。