Hari ini saya belajar tentang Session dan Cookie di Laravel — dua mekanisme yang seringkali dicampuradukkan tapi sebenarnya punya peran berbeda. Setelah sebelumnya saya sudah paham bagaimana Laravel memproses request (Routing → Controller), sekarang saya ingin tahu bagaimana framework ini mengingat identitas dan kondisi pengguna di antara request-request yang berbeda.
Bayangkan sebuah toko online. Ketika saya masuk pertama kali, petugas tidak mengenal saya. Tapi setelah saya memberikan kartu identitas (login), petugas memberikan saya kartu nama khusus (session ID). Setiap kali saya datang lagi dan menunjukkan kartu itu, petugas langsung tahu saya siapa, apa yang ada di keranjang belanja saya, dan preferensi saya. Inilah yang dilakukan session — menjaga konteks pengguna tetap hidup di setiap kunjungan.
Session Driver: Di Mana Laravel Menyimpan Ingatan?
Sesi pengguna harus disimpan di suatu tempat, dan Laravel memberikan beberapa pilihan driver untuk ini. Konfigurasinya ada di config/session.php. Ini yang saya pelajari soal setiap driver:
| Driver | Penyimpanan | Cocok Untuk | Catatan |
|---|---|---|---|
file | File di storage/framework/sessions/ | Development, small apps | Default Laravel. Simple tapi lambat untuk banyak user |
cookie | Di browser (encrypted) | Aplikasi tanpa server storage | Ukuran cookie terbatas ~4KB |
database | Tabel di database | Multi-server deployment | Perlu migration tabel sessions |
redis | Redis server | High-traffic production | Tercepat, persistent, auto-expire |
memcached | Memcached server | High-traffic, distributed | Cache in-memory, restart = data hilang |
dynamodb | Amazon DynamoDB | AWS infrastructure | Serverless, scalable |
Untuk proyek kecil saya selama ini, file driver sudah cukup. Tapi ketika saya coba deploy ke production yang menggunakan 2 server, saya baru sadar bahwa file session tidak bisa dibagikan antar server. Saat user login di server A, server B tidak punya file session yang sama. Solusinya: gunakan database atau redis.
Memahami config/session.php
File konfigurasi session ini penting untuk dipahami, bukan sekadar dibiarkan default. Beberapa pengaturan yang perlu diperhatikan:
// config/session.php
return [
'driver' => env('SESSION_DRIVER', 'file'),
// Lifetime session dalam menit. 120 = 2 jam
'lifetime' => env('SESSION_LIFETIME', 120),
// Apakah session expired saat browser ditutup?
'expire_on_close' => false,
// Encrypt semua data session (direkomendasikan aktif)
'encrypt' => env('SESSION_ENCRYPT', true),
// Nama cookie session
'connection' => env('SESSION_CONNECTION'),
// Table name untuk database driver
'table' => env('SESSION_TABLE', 'sessions'),
'store' => env('SESSION_STORE', null),
// Pengaturan cookie
'cookie' => env(
'SESSION_COOKIE',
laravel_session()
),
// Path cookie — '/' berarti berlaku untuk seluruh domain
'path' => env('SESSION_PATH', '/'),
// Domain cookie — kosongkan untuk current domain
'domain' => env('SESSION_DOMAIN', null),
// HttpOnly: JavaScript tidak bisa akses cookie (keamanan!)
'http_only' => env('SESSION_HTTP_ONLY', true),
// Secure: cookie hanya dikirim via HTTPS
'secure' => env('SESSION_SECURE_COOKIE'),
// SameSite: lax, strict, atau none
'same_site' => env('SESSION_SAME_SITE', 'lax'),
];Saya belajar bahwa encrypt: true membuat semua data session terenkripsi secara otomatis. Artinya jika saya menyimpan data sensitif di session, data tersebut aman dari manipulasi di sisi klien. Pengaturan http_only: true memastikan cookie session tidak bisa diakses oleh JavaScript — ini penting untuk mencegah serangan XSS.
Bekerja dengan Session API
Laravel menyediakan beberapa cara untuk mengakses session. Saya bisa menggunakan global helper session(), atau melalui Request instance:
// Cara 1: Global helper (paling umum saya pakai)
session()->put('key', 'value');
$value = session()->get('key');
// Cara 2: Melalui Request instance (di controller)
public function index(Request $request)
{
$request->session()->put('key', 'value');
$value = $request->session()->get('key');
}
// Cara 3: Facade
use Illuminate\Support\Facades\Session;
Session::put('key', 'value');Metode-metode session yang sering saya gunakan:
// put: menyimpan data
session()->put('user_id', $user->id);
// get: mengambil data (dengan default value opsional)
$userId = session()->get('user_id', null);
// has: mengecek apakah key ada
if (session()->has('user_id')) {
// user sudah login
}
// pull: ambil HAPUS dari session
$userId = session()->pull('user_id'); // setelah ini, key hilang
// forget: hapus satu key
session()->forget('user_id');
// flush: hapus SEMUA data session
session()->flush();
// invalidate: flush + generate session ID baru (untuk keamanan pasca-login)
session()->invalidate();Saya belajar bahwa invalidate() lebih aman dari flush() saja. flush() hanya menghapus data, tapi session ID tetap sama — rentan terhadap session fixation. invalidate() menghapus data DAN generate session ID baru, sehingga attacker yang sebelumnya mencatat session ID lama tidak bisa menggunakannya lagi. Ini yang saya pakai saat logout:
// Di AuthController.php
public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}Flash Data: Pesan Sekali Baca
Flash data adalah fitur session yang sangat sering saya gunakan — terutama untuk pesan notifikasi setelah melakukan aksi. Konsepnya sederhana: data yang disimpan hanya bertahan untuk satu request berikutnya, lalu otomatis hilang.
Bayangkan saya menghapus sebuah post di dashboard. Setelah berhasil, saya redirect ke halaman daftar post dengan pesan “Post berhasil dihapus”. Pesan ini hanya perlu muncul satu kali di halaman tujuan. Jika saya refresh halaman, pesan sudah tidak ada lagi. Inilah flash data.
// Di Controller: simpan flash message sebelum redirect
public function destroy(Post $post)
{
$post->delete();
return redirect()->route('posts.index')
->with('success', 'Post berhasil dihapus!');
}
// Atau dengan Session facade
Session::flash('error', 'Terjadi kesalahan!');
// Di Blade view: tampilkan flash message
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endifCara kerjanya: with('success', '...') sebenarnya menyimpan data ke session dengan key success. Di request berikutnya, data itu ada. Tapi setelah request itu selesai, Laravel otomatis menghapus flash data. Efeknya: pesan hanya tampil sekali.
session()->now(): Flash untuk Request Sekarang
Ada juga session()->now() yang berbeda dari flash(). Biasanya flash data baru muncul di request BERIKUTNYA. Tapi now() membuat data langsung tersedia di request SAAT INI juga. Ini berguna saat saya redirect dan ingin menampilkan pesan di view yang sama:
// Flash (default): data tersedia di request BERIKUTNYA
session()->flash('message', 'Hello!');
// Now: data tersedia di request INI juga
session()->now('message', 'Hello!');Reflash: Pertahankan Flash Data
Kadang saya redirect dua kali — misalnya dari controller ke middleware, lalu ke view. Flash data sudah terhapus di request pertama, padahal belum sempat ditampilkan. session()->reflash() memperpanjang umur flash data satu request lagi:
// Di middleware atau controller, pertahankan semua flash data session()->reflash(); // Atau pertahankan hanya beberapa key tertentu session()->reflash(['success', 'warning']);
Cookie: Identitas yang Dibawa Browser
Cookie adalah potongan kecil data yang disimpan di browser pengguna. Berbeda dari session yang datanya ada di server (atau Redis/database), cookie dikirim bolak-balik antara browser dan server di setiap request. Laravel mengelola cookie default untuk session — tapi saya juga bisa membuat cookie sendiri.
// Membuat cookie custom dengan response
return response('Hello World')
->cookie('preferences', 'dark-mode', 60 * 24 * 7); // 7 hari
// Lebih lengkap dengan opsi keamanan
return response('Hello World')
->cookie(
'preferences', // nama cookie
'dark-mode', // nilai
60 * 24 * 7, // lifetime dalam menit
'/', // path
'.example.com', // domain
true, // secure (hanya HTTPS)
true // http_only (tanpa akses JS)
);
// Menggunakan Cookie facade
use Illuminate\Support\Facades\Cookie;
Cookie::queue('theme', 'dark', 60 * 24);
// Membaca cookie di controller
$value = $request->cookie('preferences');
// Atau
$value = Cookie::get('preferences');Keamanan Cookie: Tiga Bendera yang Wajib Dipahami
Saat saya pertama kali mendengar tentang “bendera keamanan cookie”, saya sempat bingung. Ternyata istilah ini merujuk pada tiga atribut penting yang menentukan bagaimana browser menangani cookie:
// HttpOnly: JavaScript TIDAK bisa mengakses cookie ini // Proteksi terhadap XSS (Cross-Site Scripting) 'encrypt' => true, // Secure: Cookie hanya dikirim melalui HTTPS // Proteksi terhadap eavesdropping di HTTP biasa 'secure' => true, // SameSite: Kontrol kapan cookie dikirim cross-origin // 'lax' (default), 'strict', atau 'none' 'same_site' => 'lax',
HttpOnly: Ketika diaktifkan, JavaScript di browser tidak bisa membaca atau memanipulasi cookie ini. Ini mencegah serangan XSS mencuri session ID. Saya belajar bahwa di Laravel, pengaturan ini aktif secara default — bagus.
Secure: Cookie hanya akan dikirim ke server jika koneksi menggunakan HTTPS. Di development (localhost), kita bisa biarkan null agar tetap berfungsi. Tapi di production, ini WAJIB diaktifkan. Laravel otomatis mengaktifkannya jika aplikasi menggunakan HTTPS.
SameSite: Pengaturan ini mengontrol kapan browser mengirimkan cookie saat ada request lintas domain. Ada tiga opsi:
lax(default): Cookie dikirim saat user mengklik link dari situs lain, tapi TIDAK saat ada form submit atau AJAX cross-origin. Keseimbangan keamanan dan UX.strict: Cookie TIDAK PERNAH dikirim saat ada request dari situs lain. Sangat aman, tapi bisa mengganggu UX — misalnya user mengklik link dari WhatsApp langsung ke halaman internal, mereka harus login ulang.none: Cookie selalu dikirim. HARUS disertaisecure: true. Digunakan untuk cross-site integrasi, tapi jarang diperlukan.
Praktik: Flash Message System yang Rapi
Setelah memahami session dan cookie, saya mencoba membuat sistem flash message yang bisa digunakan di seluruh aplikasi. Ini hasilnya:
// app/Http/Controllers/Controller.php (base controller)
// Helper method untuk semua controller
protected function withSuccess(string $message, string $redirect = 'index')
{
return redirect()->route($redirect)
->with('success', $message);
}
protected function withError(string $message, string $redirect = 'index')
{
return redirect()->route($redirect)
->with('error', $message);
}
// Contoh penggunaan di PostController
public function store(StorePostRequest $request)
{
$post = Post::create($request->validated());
return $this->withSuccess(
'Post "' . $post->title . '" berhasil dibuat!',
'posts.index'
);
}
public function destroy(Post $post)
{
$title = $post->title;
$post->delete();
return $this->withSuccess(
"Post \"$title\" berhasil dihapus!",
'posts.index'
);
}Di Blade, saya buat komponen untuk menampilkan semua jenis flash message dengan Bootstrap alert:
<!-- resources/views/components/alert.blade.php -->
@php
$alerts = [
'success' => 'alert-success',
'error' => 'alert-danger',
'warning' => 'alert-warning',
'info' => 'alert-info',
];
@endphp
@foreach ($alerts as $key => $class)
@if (session($key))
<div class="alert {{ $class }} alert-dismissible fade show"
role="alert">
{{ session($key) }}
<button type="button" class="btn-close"
data-bs-dismiss="alert"></button>
</div>
@endif
@endforeachSolusi ini saya pakai di proyek manajemen tugas saya. Setiap aksi — buat, edit, hapus — menghasilkan pesan konfirmasi yang tepat. Flash data bekerja sempurna: pesan muncul sekali di halaman tujuan, lalu hilang saat user navigasi ke halaman lain.
Sesi Lifetime dan Expiry Management
Salah satu hal yang saya pelajari dari pengalaman production: session yang terlalu pendek membuat user harus login ulang terus-meneral, sementara session yang terlalu panjang meningkatkan risiko keamanan. Laravel memudahkan pengelolaan ini.
// Mengubah lifetime session secara dinamis (misal untuk "Remember Me")
public function login(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
if (Auth::attempt($credentials, $request->boolean('remember'))) {
$request->session()->regenerate();
// Jika "remember me" dicentang, set lifetime lebih lama
if ($request->boolean('remember')) {
config(['session.lifetime' => 60 * 24 * 30]); // 30 hari
}
return redirect()->intended('dashboard');
}
return back()->withErrors([
'email' => 'Email atau password salah.',
]);
}Saya juga belajar tentang regenerate() — method ini membuat session ID baru setelah login. Ini standar keamanan untuk mencegah session fixation: sebelum login, attacker mungkin sudah mencatat session ID user. Dengan regenerate, session ID berubah setelah autentikasi, dan session ID lama menjadi tidak valid.
Ringkasan: Session vs Cookie
Setelah belajar tentang keduanya, saya merangkum perbedaan utama session dan cookie di Laravel:
| Aspek | Session | Cookie |
|---|---|---|
| Penyimpanan | Server (file/database/Redis) | Browser (klien) |
| Keamanan | Data tidak meninggalkan server | Data dikirim bolak-balik |
| Ukuran | Tidak terbatas (server storage) | Maks ~4KB |
| Lifetime | Dikelola server, bisa expire | Dikelola browser, bisa persistent |
| Penggunaan di Laravel | session() helper | Cookie facade / response()->cookie() |
Pelajaran terpenting bagi saya: selalu aktifkan encrypt, http_only, dan secure di config session untuk production. Laravel sudah memberikan default yang aman, dan saya tidak perlu mengubahnya kecuali ada alasan spesifik. Keamanan session adalah fondasi dari keseluruhan keamanan aplikasi web — jika session bisa dicuri, semua upaya login dan authorization menjadi sia-sia.
Komentar Terbaru