DEV Community

Drian
Drian

Posted on

DOKUMEN STANDAR: Laravel Safe Email System

Infrastruktur: Shared Hosting (Hostinger/Qwords)

Framework: Laravel 8, 9, 10, atau 11

Tujuan: Mencegah blokir SMTP dengan kebijakan Zero Bounce.


BAGIAN 1: Fondasi Infrastruktur (Wajib)

Sebelum menulis kode Laravel, Anda harus memastikan domain Anda memiliki "Surat Izin Mengemudi" di internet. Tanpa ini, validasi Laravel tidak ada gunanya karena email tetap akan ditolak.

Langkah Konfigurasi di Panel Hosting:

  1. Masuk ke hPanel (Hostinger) atau cPanel (Qwords).
  2. Cari menu Email Deliverability atau DNS Zone Editor.
  3. Pastikan dua record ini aktif:
    • SPF (Sender Policy Framework): Mengizinkan IP hosting mengirim email.
    • DKIM (DomainKeys Identified Mail): Menandatangani email agar tidak dianggap palsu.

Peringatan: Jika status SPF/DKIM belum "Active/Valid" (berwarna hijau), jangan lanjutkan ke tahap coding. Hubungi CS Hosting Anda jika bingung cara mengaktifkannya.


BAGIAN 2: Validasi Tingkat Lanjut (The Gatekeeper)

Di Laravel, kita tidak validasi di Controller, melainkan menggunakan Form Request agar kode bersih dan reusable. Kita akan menggunakan fitur validasi DNS bawaan Laravel.

1. Buat Request Validation

Jalankan perintah terminal:

php artisan make:request StoreUserRequest
Enter fullscreen mode Exit fullscreen mode

2. Terapkan Aturan Ketat

Buka file app/Http/Requests/StoreUserRequest.php dan ubah menjadi seperti ini:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class StoreUserRequest extends FormRequest
{
    public function authorize()
    {
        return true; // Pastikan true agar bisa dipakai
    }

    public function rules()
    {
        return [
            'email' => [
                'required',
                'string',
                // -------------------------------------------------------
                // LAPIS 1: Syntax Check
                // 'rfc' memeriksa kesesuaian format standar internet (RFC)
                // -------------------------------------------------------
                // LAPIS 2: DNS Check (Paling Penting)
                // 'dns' melakukan ping ke domain penerima (MX Record).
                // Jika domain mati/palsu, validasi ini akan GAGAL.
                // -------------------------------------------------------
                'email:rfc,dns', 

                // LAPIS 3: Blacklist Manual (Untuk bot/spam user umum)
                'not_in:test@gmail.com,admin@gmail.com,user@example.com,root@localhost',

                // LAPIS 4: Database Check (Agar tidak duplikat)
                'unique:users,email', 
            ],
            // Validasi input lainnya (misal file upload)
            'file' => 'required|file|max:2048',
        ];
    }

    public function messages()
    {
        return [
            'email.required' => 'Email wajib diisi.',
            'email.email' => 'Format email tidak valid.',
            // Pesan ini muncul jika domain penerima mati/tidak ada
            'email.dns' => 'Domain email tidak ditemukan atau tidak aktif. Mohon gunakan email asli.',
            'email.not_in' => 'Alamat email sistem/testing tidak diizinkan.',
            'email.unique' => 'Email ini sudah terdaftar.',
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Opsi Tambahan: Anti-Disposable Email (Opsional)

Jika Anda ingin memblokir email sementara (seperti yopmail), Anda bisa menginstal package tambahan. Namun, jika ingin tanpa package (hemat resource), Anda bisa membuat Custom Rule.

Buat Rule:

php artisan make:rule DisposableEmail
Enter fullscreen mode Exit fullscreen mode

Edit app/Rules/DisposableEmail.php:

public function passes($attribute, $value)
{
    $domain = substr(strrchr($value, "@"), 1);
    // Daftar domain sampah (Anda bisa update manual atau ambil dari API/Json)
    $blockedDomains = ['yopmail.com', 'mailinator.com', '10minutemail.com', 'tempmail.com'];

    return !in_array(strtolower($domain), $blockedDomains);
}

public function message()
{
    return 'Email sementara (disposable) tidak diizinkan.';
}
Enter fullscreen mode Exit fullscreen mode

Pasang di Request tadi:

use App\Rules\DisposableEmail;

'email' => ['required', 'email:rfc,dns', new DisposableEmail],
Enter fullscreen mode Exit fullscreen mode

BAGIAN 3: Implementasi di Controller

Sekarang controller Anda bersih. Jika kode masuk ke dalam method controller, berarti email DIJAMIN valid, aktif, dan bukan spam.

use App\Http\Requests\StoreUserRequest;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeEmail;

public function register(StoreUserRequest $request)
{
    // 1. Upload File (Aman, karena email sudah valid)
    $path = $request->file('file')->store('uploads');

    // 2. Simpan User
    $user = User::create([
        'email' => $request->email,
        // ...
    ]);

    // 3. Kirim Email Menggunakan QUEUE (Wajib untuk Shared Hosting)
    // Jangan pakai send(), pakai queue()
    Mail::to($user->email)->queue(new WelcomeEmail($user));

    return response()->json(['message' => 'Registrasi sukses! Cek email Anda.']);
}
Enter fullscreen mode Exit fullscreen mode

BAGIAN 4: Mengatur Antrean (Queue) di Shared Hosting

Shared hosting memiliki batas waktu eksekusi (timeout) dan batas pengiriman email per jam. Mengirim email secara langsung (send()) akan membuat loading lama dan berisiko error.

1. Ubah Driver Queue

Edit file .env:

QUEUE_CONNECTION=database
Enter fullscreen mode Exit fullscreen mode

2. Siapkan Tabel Queue

Jalankan di terminal (lokal atau SSH hosting):

php artisan queue:table
php artisan migrate
Enter fullscreen mode Exit fullscreen mode

3. Setup Worker di Shared Hosting (CRON JOB)

Karena di shared hosting Anda biasanya tidak bisa menjalankan supervisor, kita gunakan Cron Job.

  1. Masuk ke panel hosting, cari menu Cron Jobs.
  2. Buat Cron Job baru yang berjalan Setiap Menit (* * * * *).
  3. Masukkan perintah (sesuaikan path dengan hosting Anda):
/usr/bin/php /home/username_hosting/public_html/artisan queue:work --stop-when-empty --tries=3 --timeout=90
Enter fullscreen mode Exit fullscreen mode

Penjelasan Command:

  • --stop-when-empty: Worker akan berhenti jika tidak ada email antre (hemat RAM hosting).
  • --tries=3: Jika gagal kirim, coba ulang 3 kali sebelum menyerah.
  • --timeout=90: Batas waktu proses.

BAGIAN 5: Tips Keamanan Tambahan (Rate Limiting)

Untuk menghindari blokir dari Hostinger karena mengirim terlalu cepat, tambahkan jeda pada Mailable Anda.

Buka file Mail Anda (contoh: app/Mail/WelcomeEmail.php), tambahkan implementasi ShouldQueue dan properti delay:

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

// Pastikan implement ShouldQueue
class WelcomeEmail extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;

    public function build()
    {
        return $this->view('emails.welcome')
                    ->subject('Selamat Datang!');
    }
}
Enter fullscreen mode Exit fullscreen mode

Trik Delay saat mengirim:
Jika Anda melakukan blasting (kirim ke banyak orang sekaligus), beri jeda manual:

$users = User::all();
foreach ($users as $index => $user) {
    // Kirim email setiap 10 detik agar Hostinger tidak marah
    $delay = now()->addSeconds($index * 10); 
    Mail::to($user)->later($delay, new WelcomeEmail($user));
}
Enter fullscreen mode Exit fullscreen mode

RINGKASAN (Checklist)

  1. DNS: SPF & DKIM Hostinger sudah aktif.
  2. Validasi: Menggunakan email:rfc,dns di FormRequest.
  3. Queue: Menggunakan database queue, bukan sync.
  4. Cron Job: Cron job aktif untuk memproses queue di background.
  5. Testing: Sudah mencoba kirim ke email sendiri dan TIDAK mencoba ke test@gmail.com.

Dokumentasi ini sudah siap Anda gunakan sebagai pedoman standar (SOP) untuk tim development Anda.

Top comments (1)

Collapse
 
primetarget profile image
Ethan Anderson

Terima kasih sudah membahas topik niche soal email Laravel di shared hosting—jarang ada yang sedetail ini.