Nội dung bài viết
- 1.Middleware hoạt động như thế nào?
- 2.Tạo middleware mới
- 2.1Middleware chạy trước và sau controller
- 3.Đăng ký middleware trong bootstrap/app.php
- 3.1Global middleware
- 3.2Thêm vào nhóm web hoặc api
- 3.3Đặt alias
- 4.Gắn middleware vào route
- 5.Truyền tham số cho middleware
- 6.Middleware trong controller
- 7.Terminable middleware và thứ tự ưu tiên
- 8.Chuyển từ Kernel.php sang bootstrap/app.php
- 9.Kinh nghiệm từ Hozitech
Middleware trong Laravel là lớp trung gian đứng giữa request và controller, dùng để kiểm tra hoặc biến đổi request trước khi nó vào ứng dụng (và response trước khi trả về). Ví dụ có sẵn: auth chặn người chưa đăng nhập, throttle giới hạn số request, middleware chống CSRF kiểm tra token. Từ Laravel 11, middleware được đăng ký trong bootstrap/app.php thay cho app/Http/Kernel.php.
Bài viết hướng dẫn theo Laravel 13 (bản hiện hành, PHP 8.3+): tạo middleware, gắn vào route, nhóm, alias, truyền tham số, middleware trong controller và terminable middleware.

Middleware hoạt động như thế nào?
Hãy hình dung request phải đi qua nhiều lớp vỏ trước khi chạm tới controller. Mỗi lớp có thể:
- Cho đi tiếp bằng cách gọi
$next($request). - Chặn lại bằng cách trả về response khác, ví dụ chuyển hướng hoặc
abort(403). - Xử lý response sau khi controller chạy xong (middleware "after").
Thứ tự chạy: global middleware → nhóm middleware (web hoặc api) → middleware gắn trên route → controller. Toàn bộ vòng đời này được mô tả chi tiết trong bài vòng đời request trong Laravel.
Những việc nên làm bằng middleware: kiểm tra đăng nhập, phân quyền theo vai trò, giới hạn tần suất, đặt ngôn ngữ theo URL, ghi log request, thêm header bảo mật. Kiểm tra dữ liệu form thì không nên làm ở middleware mà dùng validation hoặc Form Request.
Tạo middleware mới
php artisan make:middleware EnsureUserIsActive
Lệnh tạo file app/Http/Middleware/EnsureUserIsActive.php. Ví dụ: chặn tài khoản đã bị khóa, đăng xuất và đưa về trang đăng nhập.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserIsActive
{
public function handle(Request $request, Closure $next): Response
{
if ($request->user() && ! $request->user()->is_active) {
Auth::logout();
return redirect()->route('login')
->withErrors(['email' => 'Tài khoản của bạn đã bị khóa.']);
}
return $next($request);
}
}
Middleware chạy trước và sau controller
Đặt logic trước $next($request) để chạy trước controller. Muốn xử lý sau, lấy response về rồi mới thao tác:
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
return $response;
}
Đăng ký middleware trong bootstrap/app.php
Laravel 11, 12, 13 không còn app/Http/Kernel.php. Các thuộc tính $middleware, $middlewareGroups, $routeMiddleware (Laravel 10 đổi tên thành $middlewareAliases) trong các bài cũ được thay bằng phương thức trong closure withMiddleware().
Global middleware
// bootstrap/app.php
use App\Http\Middleware\EnsureUserIsActive;
use Illuminate\Foundation\Configuration\Middleware;
->withMiddleware(function (Middleware $middleware): void {
$middleware->append(EnsureUserIsActive::class); // thêm vào cuối
// $middleware->prepend(...); // thêm vào đầu
})
Global middleware chạy với mọi request, kể cả API. Ví dụ trên hợp lý hơn khi thêm vào nhóm web.
Thêm vào nhóm web hoặc api
Nhóm web mặc định gồm mã hóa cookie, session, chia sẻ lỗi validation cho view, chống CSRF và route model binding; nhóm api chỉ có route model binding. Thêm middleware của bạn vào nhóm:
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [
EnsureUserIsActive::class,
]);
// Nhóm tự đặt tên
$middleware->appendToGroup('admin', [
'auth',
EnsureUserIsActive::class,
]);
})
Đặt alias
Alias giúp gọi middleware bằng tên ngắn. Laravel có sẵn các alias như auth, guest, verified, can, throttle, signed.
->withMiddleware(function (Middleware $middleware): void {
$middleware->alias([
'active' => EnsureUserIsActive::class,
]);
})
Gắn middleware vào route
use App\Http\Controllers\DashboardController;
use App\Http\Middleware\EnsureUserIsActive;
use Illuminate\Support\Facades\Route;
// Một route, dùng class hoặc alias
Route::get('/dashboard', [DashboardController::class, 'index'])
->middleware(['auth', EnsureUserIsActive::class]);
// Cả nhóm route
Route::middleware(['auth', 'active'])->prefix('admin')->group(function () {
Route::get('/', [DashboardController::class, 'index']);
// Bỏ một middleware cho riêng route này
Route::get('/help', [DashboardController::class, 'help'])
->withoutMiddleware(['active']);
});
withoutMiddleware() chỉ gỡ được middleware gắn trên route, không gỡ được global middleware. Cách khai báo route, prefix và nhóm route có trong bài route trong Laravel.
Truyền tham số cho middleware
Tham số được nhận sau $next, khai báo trên route bằng dấu hai chấm, nhiều tham số cách nhau bằng dấu phẩy:
class EnsureUserHasRole
{
public function handle(Request $request, Closure $next, string ...$roles): Response
{
if (! in_array($request->user()?->role, $roles, true)) {
abort(403);
}
return $next($request);
}
}
Route::put('/posts/{post}', [PostController::class, 'update'])
->middleware(EnsureUserHasRole::class.':editor,admin');
Với quyền gắn với từng bản ghi (chỉ tác giả được sửa bài của mình), nên dùng Policy và middleware can thay vì tự viết, xem bài authentication và authorization trong Laravel.
Middleware trong controller
Từ Laravel 11, lớp Controller cơ sở không còn phương thức middleware(), nên kiểu viết $this->middleware('auth') trong __construct() sẽ lỗi. Thay vào đó, controller implement interface HasMiddleware:
<?php
namespace App\Http\Controllers;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
class PostController extends Controller implements HasMiddleware
{
public static function middleware(): array
{
return [
'auth',
new Middleware('active', except: ['index', 'show']),
new Middleware('throttle:10,1', only: ['store']),
];
}
// ...
}
Laravel 13 có thêm cách khai báo bằng attribute PHP, đặt trên class hoặc từng phương thức:
use Illuminate\Routing\Attributes\Controllers\Middleware;
#[Middleware('auth')]
class PostController
{
#[Middleware('throttle:10,1')]
public function store()
{
// ...
}
}
Xem thêm cách tổ chức controller trong bài controller trong Laravel.
Terminable middleware và thứ tự ưu tiên
Nếu middleware có phương thức terminate(), Laravel gọi nó sau khi response đã gửi tới trình duyệt (với máy chủ dùng FastCGI). Phù hợp cho việc ghi log hoặc thống kê không cần người dùng chờ:
public function terminate(Request $request, Response $response): void
{
logger()->info('Request xong', [
'url' => $request->fullUrl(),
'status' => $response->getStatusCode(),
]);
}
Khi cần middleware của bạn luôn chạy trước hoặc sau một middleware nào đó dù được gắn theo thứ tự nào, dùng $middleware->prependToPriorityList(before: ..., prepend: ...) hoặc appendToPriorityList(after: ..., append: ...) trong bootstrap/app.php.
Chuyển từ Kernel.php sang bootstrap/app.php
| Laravel 10 trở về trước (Kernel.php) | Laravel 11, 12, 13 (bootstrap/app.php) |
|---|---|
$middleware | $middleware->append() / prepend() |
$middlewareGroups['web'] | $middleware->web(append: [...]) |
$routeMiddleware / $middlewareAliases | $middleware->alias([...]) |
$middlewarePriority | $middleware->priority([...]) |
$this->middleware() trong constructor controller | Interface HasMiddleware hoặc attribute #[Middleware] |
Dự án nâng cấp từ Laravel 10 vẫn có thể giữ cấu trúc cũ; cấu trúc mới áp dụng cho dự án tạo mới hoặc khi bạn chủ động chuyển đổi.
Kinh nghiệm từ Hozitech
Trong các dự án Laravel của Hozitech, middleware tự viết thường ít và nhỏ: đặt ngôn ngữ theo URL, chặn tài khoản bị khóa, chuyển hướng URL cũ sau khi đổi cấu trúc website. Quy tắc chúng tôi giữ là mỗi middleware làm đúng một việc, không truy vấn nặng trong global middleware vì nó chạy với mọi request, và luôn ghi rõ middleware vào nhóm nào trong bootstrap/app.php để người bảo trì sau dễ lần theo.
Nếu website Laravel của doanh nghiệp bạn cần người bảo trì, nâng cấp phiên bản hoặc rà soát định kỳ, có thể tham khảo dịch vụ chăm sóc website.
Về tác giả
Founder
Xin chào mình là Hiền hiện đang là Founder của Hozitech. Mình là kỹ sư phần mềm có hơn 9+ năm kinh nghiệm trong lĩnh vực thiết kế website, ứng dụng di động... Gần đây mình đang nghiên cứu thêm về lĩnh vực marketing. Mình rất vui khi được chia sẽ những kiến thức và kinh nghiệm của mình đến mọi người!
Câu hỏi thường gặp