Middleware Laravel 11: Cara Kerja, Contoh Kode & Error Umum

Banyak aplikasi Laravel yang controller-nya penuh dengan pengecekan berulang: apakah pengguna sudah login, apakah akunnya aktif, apakah ia boleh membuka halaman admin. Pengecekan y...

Middleware Laravel 11: Cara Kerja, Contoh Kode & Error Umum

Banyak aplikasi Laravel yang controller-nya penuh dengan pengecekan berulang: apakah pengguna sudah login, apakah akunnya aktif, apakah ia boleh membuka halaman admin. Pengecekan yang disalin ke puluhan method seperti itu mudah terlewat satu, dan satu yang terlewat bisa menjadi celah keamanan. Middleware menyelesaikan masalah ini dengan memindahkan aturan lintas-fitur ke satu tempat yang dilewati setiap request.

Artikel ini membahas cara kerja middleware di Laravel 11 (PHP 8.2+): posisinya dalam siklus request, cara membuat dan mendaftarkannya, perbedaan before dan after, parameter, terminable middleware, urutan eksekusi, sampai error yang paling sering muncul beserta solusinya.

1. Posisi Middleware dalam Siklus Request

Ketika request masuk lewat public/index.php, Laravel membuat instance aplikasi lalu menyerahkan request ke HTTP kernel. Kernel meneruskan request melewati tumpukan middleware secara berlapis, mirip bawang. Alurnya:

  1. Request masuk ke middleware global (misalnya pemangkas spasi pada input, pengecekan mode maintenance).
  2. Router mencocokkan URL dengan route, lalu menjalankan middleware grup (web atau api) dan middleware milik route itu.
  3. Controller atau closure route dijalankan dan menghasilkan response.
  4. Response berjalan balik melewati lapisan yang sama dengan urutan terbalik.
  5. Response dikirim ke browser, lalu method terminate() (jika ada) dijalankan.

Karena berlapis, satu middleware bisa menjalankan kode sebelum request diteruskan dan sesudah response terbentuk. Inilah yang membuat middleware cocok untuk autentikasi, logging, header keamanan, dan pengaturan bahasa.

2. Membuat Middleware Pertama

Buat kerangka middleware dengan artisan:

php artisan make:middleware EnsureUserIsActive

File baru muncul di app/Http/Middleware/EnsureUserIsActive.php. Isi method handle():

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureUserIsActive
{
    public function handle(Request $request, Closure $next): Response
    {
        $user = $request->user();

        if ($user === null || ! $user->is_active) {
            auth()->logout();

            return redirect()->route('login')
                ->with('error', 'Akun Anda tidak aktif. Hubungi admin.');
        }

        return $next($request);
    }
}

Kuncinya ada di $next. Jika Anda mengembalikan $next($request), request lanjut ke lapisan berikutnya. Jika Anda mengembalikan redirect atau memanggil abort() lebih dulu, request berhenti di sini dan controller tidak pernah dijalankan.

3. Mendaftarkan Middleware di Laravel 11

Sejak Laravel 11, file app/Http/Kernel.php tidak ada lagi di proyek baru. Semua pendaftaran dilakukan di bootstrap/app.php:

use Illuminate\Foundation\Configuration\Middleware;

->withMiddleware(function (Middleware $middleware) {
    // alias untuk dipakai per-route
    $middleware->alias([
        'active' => \App\Http\Middleware\EnsureUserIsActive::class,
        'role'   => \App\Http\Middleware\EnsureUserHasRole::class,
    ]);

    // global: berjalan untuk SEMUA request
    $middleware->append(\App\Http\Middleware\AddSecurityHeaders::class);

    // hanya grup web
    $middleware->web(append: [
        \App\Http\Middleware\SetLocale::class,
    ]);
})

Proyek yang di-upgrade dari Laravel 10 masih boleh memakai Kernel.php lama. Jangan mencampur dua cara dalam satu proyek karena akan membingungkan tim.

Menerapkan ke Route dan Grup

Route::get('/dashboard', [DashboardController::class, 'index'])
    ->middleware(['auth', 'active']);

Route::middleware(['auth', 'active'])->group(function () {
    Route::resource('products', ProductController::class);

    // kecualikan satu route dari middleware grup
    Route::get('/products/export', [ProductController::class, 'export'])
        ->withoutMiddleware('active');
});

4. Before vs After Middleware

Perbedaannya hanya pada letak kode Anda terhadap pemanggilan $next($request).

JenisKapan berjalanContoh pemakaian
BeforeSebelum $next() dipanggilCek login, cek role, validasi API key, set locale
AfterSetelah $next() mengembalikan responseTambah header keamanan, catat status code, kompres output
TerminableSetelah response terkirim ke browserSimpan log durasi, kirim metrik, bersih-bersih sesi

Contoh after middleware yang menambahkan header keamanan dasar:

public function handle(Request $request, Closure $next): Response
{
    $response = $next($request);

    $response->headers->set('X-Frame-Options', 'SAMEORIGIN');
    $response->headers->set('X-Content-Type-Options', 'nosniff');
    $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');

    return $response;
}

5. Middleware dengan Parameter

Pola yang paling sering dipakai adalah gerbang berbasis peran. Parameter ditulis setelah $next, dan dengan variadic Anda bisa menerima beberapa nilai sekaligus:

public function handle(Request $request, Closure $next, string ...$roles): Response
{
    $user = $request->user();

    if ($user === null || ! in_array($user->role, $roles, true)) {
        abort(403, 'Anda tidak punya akses ke halaman ini.');
    }

    return $next($request);
}

Dari route, kirim nilainya dengan titik dua dan pisahkan dengan koma:

Route::get('/laporan', [ReportController::class, 'index'])
    ->middleware(['auth', 'role:admin,keuangan']);

Kalau aturan aksesnya mulai rumit (izin per fitur, bukan sekadar peran), lebih rapi memakai package permission atau Gate/Policy daripada menumpuk logika di middleware.

6. Terminable Middleware

Ada pekerjaan yang tidak perlu membuat pengguna menunggu, misalnya mencatat berapa lama request diproses. Tambahkan method terminate():

public function handle(Request $request, Closure $next): Response
{
    $request->attributes->set('started_at', microtime(true));

    return $next($request);
}

public function terminate(Request $request, Response $response): void
{
    $ms = (microtime(true) - $request->attributes->get('started_at')) * 1000;

    if ($ms > 1000) {
        logger()->warning('Request lambat', [
            'path' => $request->path(),
            'ms'   => round($ms),
        ]);
    }
}

Catatan: terminate() benar-benar berjalan setelah response terkirim hanya bila server memakai PHP-FPM. Di beberapa shared hosting perilakunya bisa berbeda, jadi jangan taruh pekerjaan berat di sana. Untuk tugas berat, gunakan queue.

7. Urutan Eksekusi Itu Penting

Middleware dijalankan sesuai urutan di array. Jika middleware Anda membaca $request->user() tetapi diletakkan sebelum auth, hasilnya selalu null. Laravel punya daftar prioritas bawaan, dan Anda bisa menambah aturan sendiri:

$middleware->priority([
    \Illuminate\Session\Middleware\StartSession::class,
    \Illuminate\Auth\Middleware\Authenticate::class,
    \App\Http\Middleware\EnsureUserIsActive::class,
]);

8. Kapan Pakai Middleware, Kapan Tidak

  • Pakai middleware untuk aturan yang berlaku ke banyak route: login, status akun, bahasa, header, pembatasan request (throttle:60,1).
  • Pakai Policy untuk aturan yang bergantung pada data spesifik, misalnya "pengguna hanya boleh mengedit invoice miliknya".
  • Pakai Form Request untuk validasi input, bukan middleware.
  • Pakai queue untuk pekerjaan lambat seperti kirim email atau resize gambar.

9. Error Umum dan Cara Memperbaikinya

  • Halaman kosong atau error "must be of type Response, null returned": Anda lupa return $next($request). Pastikan setiap jalur kode mengembalikan response.
  • "Target class [role] does not exist": alias belum didaftarkan di bootstrap/app.php, atau cache konfigurasi lama. Jalankan php artisan optimize:clear.
  • Redirect loop ke /login: middleware pengecek aktif juga terpasang di route login. Kecualikan route itu dengan withoutMiddleware().
  • Aplikasi melambat: ada query database di middleware global. Pindahkan ke middleware route atau cache hasilnya.

10. Menguji Middleware dengan Feature Test

Middleware yang mengatur akses sebaiknya punya test, karena kesalahan kecil di sini langsung berarti data bisa dibuka orang yang tidak berhak. Test berikut memastikan pengguna nonaktif dikeluarkan, sedangkan pengguna aktif bisa masuk dashboard:

use App\Models\User;

it('menendang pengguna nonaktif ke halaman login', function () {
    $user = User::factory()->create(['is_active' => false]);

    $this->actingAs($user)
        ->get('/dashboard')
        ->assertRedirect(route('login'));
});

it('mengizinkan pengguna aktif membuka dashboard', function () {
    $user = User::factory()->create(['is_active' => true]);

    $this->actingAs($user)
        ->get('/dashboard')
        ->assertOk();
});

Contoh di atas memakai Pest, yang menjadi pilihan default saat membuat proyek Laravel baru. Jika Anda memakai PHPUnit, isinya sama, hanya dibungkus method di kelas test. Saat menguji fitur lain yang tidak berhubungan dengan akses, Anda bisa melewati middleware tertentu dengan $this->withoutMiddleware(EnsureUserIsActive::class) agar test tetap fokus pada hal yang sedang diuji.

Satu kebiasaan yang membantu: setiap kali menambah route admin baru, tambahkan juga satu test yang memastikan pengguna biasa mendapat 403. Test seperti ini murah dibuat dan mencegah regresi ketika ada orang lain yang memindahkan route ke luar grup middleware.

Checklist Sebelum Deploy

  • Semua route admin memakai auth plus pengecek peran.
  • Route login, register, dan reset password tidak terkena middleware yang mewajibkan login.
  • Middleware global tidak menjalankan query berat.
  • Ada test fitur yang memastikan pengguna tanpa izin mendapat 403.
  • php artisan route:list -v sudah dicek untuk melihat middleware tiap route.

Untuk pengamanan lebih lengkap, baca 10 best practice keamanan aplikasi web dan checklist keamanan dasar Laravel. Jika Anda mempelajari source code dari GudangCode, membuka bootstrap/app.php dan routes/web.php adalah cara tercepat memahami alur aksesnya.

Ingin langsung mempraktikkannya?

Buat akun gratis untuk mengunduh contoh aplikasi Laravel, menyimpan tutorial favorit, dan mencoba kursus. Gratis, bisa daftar dengan akun Google.

Daftar Gratis Lihat katalog aplikasi
Yudhi
Ditulis oleh
Yudhi
Founder & Lead Developer, GudangCode

Yudhi adalah founder GudangCode dan developer Laravel dengan pengalaman membangun puluhan sistem informasi bisnis siap pakai — mulai dari POS, HRIS, hingga aplikasi manajemen. Ia menulis panduan dan artikel di GudangCode untuk membantu developer Indonesia menjalankan, memahami, dan men-deploy source code Laravel dengan benar.

Laravel PHP MySQL Sistem Informasi Bisnis
Lihat semua artikel Yudhi
Middleware Laravel Programming & Laravel
Bacaan Paling Dicari
📚 Free Learning Hub

Learn Coding for Free at DhieCoderWeb

Explore Laravel, PHP, JavaScript tutorials, source code, web development guides, and practical programming tips.

DhieCoderWeb
100+
Tutorials
Free
Learning
SEO
Tips
Visit Dhiecoderweb.com →