SDWebImage 是 iOS 开发中常用的异步图片加载与缓存框架。实际项目里,图片资源往往来自不同的 CDN、图片服务器或业务接口,有些服务器会根据 HTTP 请求头中的 Referer 判断请求来源。如果所有图片都使用同一个全局请求头,就可能出现部分图片加载正常、部分图片返回 403,或者不同图片源之间产生请求头冲突的问题。
这种场景下,更合理的做法是针对不同图片请求分别设置自定义 Referer,而不是修改 SDWebImage 的全局配置。核心思路是利用 SDWebImage 提供的上下文参数或自定义请求方式,在单次图片加载过程中追加 HTTP Header。
为什么需要自定义 Referer
HTTP 请求中的 Referer 用于描述当前请求通常来自哪个页面。服务器可以通过它进行来源识别、防盗链校验或访问策略控制。
例如某个图片服务器要求:
Referer: https://www.example.com/
如果客户端直接请求图片:
https://cdn.example.com/images/banner.jpg
而没有携带正确的 Referer,服务器可能返回:
HTTP 403 Forbidden
在浏览器环境中,Referer 往往由浏览器根据页面跳转关系自动处理,但 iOS App 中通过 NSURLSession、SDWebImage 等网络组件发起图片请求时,并不会天然具备网页浏览器中的 Referer 行为。
因此,对于存在防盗链机制的图片资源,需要在请求阶段主动设置。
SDWebImage 中设置请求头的基本思路
SDWebImage 的图片加载通常可以直接通过 URL 调用:
Objective-C[[SDWebImageManager sharedManager] loadImageWithURL:url options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { // 图片加载完成 }];
如果需要为当前请求增加 Referer,可以通过 SDWebImageContext 传递自定义请求相关配置。
在较新的 SDWebImage 版本中,可以使用 SDWebImageContext 对单次请求进行定制。例如:
Objective-CNSDictionary *context = @{ SDWebImageContextHTTPHeaders : @{ @"Referer": @"https://www.example.com/" } }; [imageView sd_setImageWithURL:url placeholderImage:nil options:0 context:context];
这样设置的特点是,Referer 只作用于这一次图片请求,不会影响其他图片。
这种方式特别适合一个 App 同时加载多个图片域名的情况。
不同图片使用不同 Referer
假设项目中存在两个图片来源:
https://cdn-a.example.com/ https://cdn-b.example.com/
其中 CDN A 要求:
Referer: https://a.example.com/
CDN B 要求:
Referer: https://b.example.com/
就可以根据图片 URL 动态构造请求头:
Objective-CNSURL *url = [NSURL URLWithString:@"https://cdn-a.example.com/image/test.jpg"]; NSDictionary *headers = @{ @"Referer": @"https://a.example.com/" }; NSDictionary *context = @{ SDWebImageContextHTTPHeaders: headers }; [imageView sd_setImageWithURL:url placeholderImage:nil options:0 context:context];
加载 CDN B 的图片时,只需要换成另外一个 Referer:
Objective-CNSURL *url = [NSURL URLWithString:@"https://cdn-b.example.com/image/test.jpg"]; NSDictionary *headers = @{ @"Referer": @"https://b.example.com/" }; NSDictionary *context = @{ SDWebImageContextHTTPHeaders: headers }; [imageView sd_setImageWithURL:url placeholderImage:nil options:0 context:context];
这样就可以实现“一个图片请求对应一个 Referer”的效果。
根据图片域名自动设置 Referer
如果项目中图片请求很多,不建议每次调用 sd_setImageWithURL: 时手动判断域名。可以统一封装一个方法。
例如:
Objective-C- (NSDictionary *)contextForImageURL:(NSURL *)url { NSString *host = url.host.lowercaseString; NSString *referer = nil; if ([host isEqualToString:@"cdn-a.example.com"]) { referer = @"https://a.example.com/"; } else if ([host isEqualToString:@"cdn-b.example.com"]) { referer = @"https://b.example.com/"; } if (!referer) { return nil; } return @{ SDWebImageContextHTTPHeaders : @{ @"Referer": referer } }; }
调用时:
Objective-CNSURL *url = [NSURL URLWithString:@"https://cdn-a.example.com/image/test.jpg"]; NSDictionary *context = [self contextForImageURL:url]; [imageView sd_setImageWithURL:url placeholderImage:nil options:0 context:context];
这种方式能够将“图片地址”和“请求策略”进行统一管理,后续增加新的图片服务器也更加方便。
使用 SDWebImageManager 加载图片
如果项目不是直接使用 UIImageView 分类,而是通过 SDWebImageManager 处理图片,也可以传入对应的 context。
例如:
Objective-CNSDictionary *context = @{ SDWebImageContextHTTPHeaders : @{ @"Referer": @"https://www.example.com/" } }; [[SDWebImageManager sharedManager] loadImageWithURL:url options:SDWebImageRetryFailed context:context progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { if (image) { NSLog(@"图片加载成功"); } else { NSLog(@"图片加载失败:%@", error); } }];
需要注意的是,不同 SDWebImage 大版本的 loadImageWithURL: 方法签名可能存在差异。如果项目升级过 SDWebImage,应该以当前版本头文件中的 API 为准。
为什么不建议直接修改全局 HTTP Header
一种比较容易想到的方案是直接修改全局配置,让所有图片请求都添加:
Referer: https://www.example.com/
这种方式虽然简单,但在复杂项目中容易产生副作用。
例如:
图片 A → CDN A → 要求 Referer A 图片 B → CDN B → 要求 Referer B 图片 C → 公共 CDN → 不需要 Referer
如果统一设置成:
Referer: https://a.example.com/
那么 B 的请求可能无法通过防盗链验证,甚至某些服务器会因为不符合预期的请求头而拒绝访问。
因此,如果 Referer 与资源来源存在对应关系,优先采用单请求配置更加合理。
自定义 Referer 与缓存需要注意什么
SDWebImage 不只是负责网络请求,还包含图片缓存机制。
假设同一个图片 URL:
https://cdn.example.com/image.jpg
第一次请求使用:
Referer: https://site-a.example.com/
第二次请求使用:
Referer: https://site-b.example.com/
如果服务器实际上根据 Referer 返回不同内容,那么仅仅依赖 URL 作为缓存标识就可能产生缓存语义上的问题。
也就是说:
URL 相同 Referer 不同 服务器响应可能不同
这种情况下,需要进一步确认业务是否真的需要让不同 Referer 对应不同缓存结果。
如果 Referer 只是服务器用于防盗链验证,而图片内容始终一致,通常不会产生明显问题。但如果服务器会根据请求头返回不同图片,则需要重新设计缓存 Key。
自定义 Header 不等于修改 URL
有些开发者为了绕过服务器的 Referer 检查,会尝试给 URL 添加类似:
?referer=https://example.com
这种做法与真正设置 HTTP Header 并不是一回事。
例如:
https://cdn.example.com/image.jpg?referer=https://example.com
服务器收到的是 URL 查询参数:
referer=https://example.com
而真正的 HTTP Header 是:
httpReferer: https://example.com/
两者由服务器端完全不同的代码路径处理。
如果服务器要求的是 HTTP Referer 请求头,就应该在 HTTP 请求层面设置,而不是简单修改图片 URL。
Referer 与 Referrer 的名称区别
HTTP 标准中的 Header 名称是:
httpReferer
这里的拼写本身就是 Referer,少了一个 r,属于历史标准遗留命名。
开发过程中经常会看到:
Referer Referrer
其中:
-
Referer是 HTTP Header 的实际字段名称; -
Referrer是更符合英语习惯的单词写法。
在 Objective-C 中设置请求头时,应使用:
Objective-C@"Referer"
而不是:
Objective-C@"Referrer"
否则服务器可能无法识别。
Swift 中的实现方式
如果项目使用 Swift,整体思路保持一致。
例如:
Swiftlet url = URL(string: "https://cdn.example.com/image.jpg")! let context: [SDWebImageContextOption: Any] = [ .httpHeaders: [ "Referer": "https://www.example.com/" ] ] imageView.sd_setImage( with: url, placeholderImage: nil, options: [], context: context )
具体类型名称和 API 写法可能随 SDWebImage 版本发生变化,因此实际开发时建议结合项目当前版本的 Swift 接口进行调整。
如果 HTTP Header 方式没有生效
遇到“代码已经设置 Referer,但服务器仍然返回 403”的情况,不要马上认为 SDWebImage 没有发送 Header,可以按照请求链路逐层排查。
首先确认图片请求是否真的经过 SDWebImage。
其次检查请求 Header:
Referer: https://www.example.com/
是否实际出现在 HTTP 请求中。
然后检查服务器要求的 Referer 是否完全匹配。例如服务器可能要求:
https://www.example.com
而客户端发送:
https://www.example.com/
某些服务端实现可能会进行严格匹配。
还需要确认服务器是否同时校验:
User-Agent Origin Cookie Authorization Host
因为图片返回 403 并不一定意味着 Referer 有问题。
使用代理工具验证请求
开发阶段可以通过 Charles、Proxyman 等 HTTP 调试工具观察请求。
重点检查:
Request URL Request Method Request Headers Response Status Code Response Headers
例如:
GET /image/test.jpg HTTP/1.1 Host: cdn.example.com Referer: https://www.example.com/ User-Agent: ...
如果能够看到 Referer,说明客户端至少已经将请求头发送出去。
如果没有看到,则需要继续检查 SDWebImage 版本、Context 配置以及实际使用的图片加载入口。
需要注意 HTTPS 抓包、证书配置以及 App 网络环境可能影响调试结果,正式环境不应为了抓包而长期关闭安全校验。
iOS 网络安全策略也需要检查
如果图片地址使用的是 HTTP 而不是 HTTPS,例如:
http://cdn.example.com/image.jpg
还可能受到 iOS App Transport Security(ATS)的影响。
这种情况下,即使 Referer 配置正确,也可能因为网络安全策略导致请求失败。
如果服务器支持 HTTPS,优先直接使用:
https://cdn.example.com/image.jpg
比针对 HTTP 资源放宽 ATS 限制更加合理。
Referer 并不是安全认证机制
需要特别注意,Referer 不应该被当作身份认证手段。
客户端可以自行构造:
httpReferer: https://www.example.com/
因此服务器不能仅凭 Referer 判断“请求一定来自可信客户端”。
如果图片资源涉及真正的权限控制,应使用:
HTTPS Token Cookie Authorization 签名 URL 临时访问凭证
等更可靠的机制。
Referer 更适合承担防盗链、来源识别等辅助职责。
推荐的项目封装方式
实际项目中,可以把 Referer 策略封装到一个图片加载工具里,而不是散落在大量 ViewController 中。
例如:
Objective-C+ (void)setImageWithURL:(NSURL *)url imageView:(UIImageView *)imageView { NSString *host = url.host.lowercaseString; NSString *referer = nil; if ([host isEqualToString:@"cdn-a.example.com"]) { referer = @"https://a.example.com/"; } else if ([host isEqualToString:@"cdn-b.example.com"]) { referer = @"https://b.example.com/"; } NSDictionary *context = nil; if (referer.length > 0) { context = @{ SDWebImageContextHTTPHeaders : @{ @"Referer": referer } }; } [imageView sd_setImageWithURL:url placeholderImage:nil options:0 context:context]; }
调用方只需要:
Objective-C[ImageLoader setImageWithURL:url imageView:imageView];
这样可以把图片加载、域名识别、Referer 设置集中管理,减少重复代码,也方便后续统一调整。
常见问题总结
为什么 SDWebImage 加载图片时没有 Referer?
因为 App 中的网络请求并不等同于浏览器页面请求,不能假设浏览器行为会自动出现在 SDWebImage 请求中。
能不能给不同图片设置不同 Referer?
可以。更适合使用单次请求的 Context 或自定义请求配置,根据图片 URL、域名或业务类型动态设置。
是否应该全局设置 Referer?
如果整个 App 的图片资源确实使用同一个来源规则,可以考虑统一配置;如果存在多个 CDN 或不同防盗链策略,则更适合按请求设置。
Referer 设置正确后为什么还是 403?
可能还涉及 Cookie、User-Agent、Authorization、签名参数、IP 限制、服务器白名单等条件,需要结合实际 HTTP 响应进行排查。
Referer 和 Referrer 是不是一样?
语义上都表示来源,但 HTTP 请求头字段名称使用的是 Referer。
自定义 Referer 能不能解决所有防盗链问题?
不能。服务器可能同时检查多个请求条件,而且 Referer 本身可以被客户端伪造,因此不应该作为真正的身份认证方案。
对于 SDWebImage 项目而言,处理自定义 Referer 的关键并不是简单地“给所有图片添加一个请求头”,而是建立清晰的请求策略:根据资源域名或业务来源确定 Referer,通过单次请求上下文传递 Header,同时考虑缓存、服务器校验以及 HTTPS 安全策略。这样既能满足不同图片源的防盗链要求,也能避免全局配置对其他图片请求产生意外影响。