如何在Laravel访客页面禁用Session与XSRF-TOKEN Cookie适配Cloudfront缓存
需求说明
- 运行环境:PHP 8.2,Laravel 11
- 核心目标:
- 未登录访客访问博客文章页面(如
/posts/news-item-1)时,响应头添加Cache-Control: max-age=3600, public,同时禁用laravel_session和XSRF-TOKENCookie,让Cloudfront可缓存静态内容 - 登录用户访问同一页面时,内容保持私有不缓存,保留Session和Cookie功能
- 应用其他页面正常维持Session和Cookie机制
- 未登录访客访问博客文章页面(如
现有实现的问题
当前方案通过自定义PageSpeed中间件设置cdn_cache请求参数,再重写VerifyCsrfToken和StartSession核心中间件来控制Cookie生成。虽然功能可用,但重写核心中间件会增加后续版本升级的适配成本,且逻辑分散在多个文件中,维护性较差。
更简洁优雅的实现方案
我们可以通过路由组专属中间件结合Laravel原生扩展点实现,无需重写完整的核心中间件逻辑:
1. 创建缓存控制专属中间件
新建CacheablePublicContent中间件,集中处理缓存规则和Cookie禁用标记:
namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Symfony\Component\HttpFoundation\Response; class CacheablePublicContent { public function handle(Request $request, Closure $next): Response { // 仅对未登录用户的博客文章路由生效 if (!Auth::check() && $request->is('posts/*')) { // 标记不需要Session Cookie $request->attributes->set('skip_session_cookie', true); // 标记不需要XSRF Token Cookie $request->attributes->set('skip_xsrf_cookie', true); // 添加Cloudfront缓存响应头 return $next($request)->header('Cache-Control', 'max-age=3600, public'); } // 登录用户或非博客页面,返回私有缓存头 return $next($request)->header('Cache-Control', 'private, no-cache'); } }
2. 调整VerifyCsrfToken中间件
修改VerifyCsrfToken,利用原生方法判断是否添加XSRF Cookie:
namespace App\Http\Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; class VerifyCsrfToken extends Middleware { public function shouldAddXsrfTokenCookie() { // 若标记跳过XSRF Cookie,则不生成 if ($this->request->attributes->get('skip_xsrf_cookie')) { return false; } return parent::shouldAddXsrfTokenCookie(); } }
3. 调整StartSession中间件
简化StartSession逻辑,仅在需要时添加Session Cookie:
namespace App\Http\Middleware; use Illuminate\Session\Middleware\StartSession as Middleware; class StartSession extends Middleware { protected function addCookieToResponse($response, $session) { // 若标记跳过Session Cookie,则不添加 if ($this->request->attributes->get('skip_session_cookie')) { return; } parent::addCookieToResponse($response, $session); } }
4. 绑定中间件到路由组
在routes/web.php中为博客路由组绑定该中间件:
use App\Http\Controllers\PostController; use App\Http\Middleware\CacheablePublicContent; Route::middleware(CacheablePublicContent::class)->group(function () { Route::get('/posts/{slug}', [PostController::class, 'show'])->name('posts.show'); // 其他博客相关路由 });
方案优势
- 逻辑集中:所有缓存控制和Cookie禁用逻辑都在专属中间件中,便于统一维护和调整
- 低侵入性:仅扩展核心中间件的特定方法,而非重写完整逻辑,降低Laravel版本升级时的适配风险
- 语义清晰:通过请求属性传递标记,比请求参数更符合Laravel的设计规范
内容的提问来源于stack exchange,提问作者alexmcfarlane
相关产品推荐
相关产品推荐

