temporaryUrl() 生成带签名的临时 URL,需配置支持签名的磁盘(如 s3 或启用 temporary_url 的 local),但其签名不被 signed 中间件自动校验,须配合 signed 路由和控制器收口访问。

用 temporaryUrl() 生成带签名的临时 URL
在 Laravel 中,temporaryUrl() 是最直接的方式,适用于 Storage::disk('s3') 或本地磁盘(需启用 url 驱动并配置 temporary_url 支持)。它会自动添加 signature、expires 和可选的 relative 参数,验证逻辑由 Laravel 内置中间件 Illuminate\Http\Middleware\ValidateSignature 处理。
注意:该方法仅对使用 URL::temporarySignedRoute() 或 Storage::temporaryUrl() 生成的 URL 生效,普通 url() 不参与签名验证。
- 必须使用支持签名的磁盘(如
s3、local配合url驱动 +temporary_url配置) - 本地磁盘默认不支持
temporaryUrl(),需在config/filesystems.php中为该磁盘显式设置'url' => env('APP_URL'), 'temporary_url' => true - 生成的 URL 有效期从服务器当前时间起算,不依赖客户端时钟
签名 URL 必须通过 signed 中间件校验
所有签名 URL 的请求都会被 Laravel 自动拦截并校验签名与过期时间,前提是路由已绑定 signed 中间件。Laravel 不会自动为 Storage::temporaryUrl() 生成的链接强制走该流程——它只对通过 URL::temporarySignedRoute() 生成的路由生效。所以,如果你用 temporaryUrl() 生成的是文件直链(如 S3),你得自己在对应控制器里手动校验。
正确做法是:把文件访问收口到一个带签名路由的控制器方法中,例如:
Route::get('/download/{filename}', [DownloadController::class, 'show'])
->name('download.show')
->middleware('signed');
然后在控制器中用 Storage::url() 或直接返回 response()->file(),而不是暴露原始 temporaryUrl() 给前端。
-
signed中间件只检查signature和expires查询参数,不校验路径或文件名 - 若绕过路由直接访问
temporaryUrl()返回的 S3 链接,签名机制完全失效——S3 不认识 Laravel 的签名规则 - 自定义校验逻辑可用
URL::hasValidSignature($request)手动触发
URL::temporarySignedRoute() 更适合控制访问入口
当你要保护的是「某个操作行为」(如下载、预览、重置),而不是单纯一个文件路径,优先用 URL::temporarySignedRoute()。它把签名逻辑和路由绑定在一起,天然适配 signed 中间件,且不依赖存储驱动能力。
示例:生成一个 30 分钟有效的下载链接
use Illuminate\Support\Facades\URL;
$url = URL::temporarySignedRoute(
'download.show',
now()->addMinutes(30),
['filename' => 'report.pdf']
);
此时 $url 类似 /download/report.pdf?expires=1717023600&signature=abc123...,访问时会经由 signed 中间件自动校验。
- 路由名必须已在
routes/web.php中定义,并显式绑定signed中间件 - 签名基于当前
APP_KEY和完整查询字符串生成,篡改任意参数(包括filename)都会导致验证失败 - 不能用于大文件流式响应场景(如视频播放),因为每次请求都走 PHP 控制器,有内存和超时风险
签名失效的常见原因和调试建议
签名 URL 访问返回 403 最常见的不是代码写错,而是环境或配置偏差。
- 服务器时间与 NTP 不同步:Laravel 校验
expires基于服务端now(),误差超过 15 秒即拒绝(默认容差);用date命令确认系统时间 -
APP_KEY在多台服务器上不一致:负载均衡下必须共用同一APP_KEY,否则签名无法互相验证 - URL 被代理或 CDN 重写:某些 CDN 会 strip 掉
signature或expires参数,或强制跳转导致 query string 丢失 - 使用了
URL::signedRoute()(无temporary)但没设过期时间:它生成的是永久签名,不推荐用于防未授权访问
调试时可在中间件中加日志,或临时在控制器里 dump $request->fullUrl() 和 URL::hasValidSignature($request, 300) 的返回值,确认是签名错还是时间错。










