Ketika aplikasi mengirim email verifikasi akun, jarak antara kode PHP dan kotak masuk pengguna ternyata cukup panjang. Pesan harus melewati beberapa server, menjalani pemeriksaan keamanan, dan lolos penyaringan spam sebelum tampil di layar penerima. Banyak pengembang baru menyadari pentingnya pemahaman ini justru setelah email aplikasinya masuk folder spam atau tidak pernah sampai. Chapter ini memetakan perjalanan sebuah email, membedakan peran SMTP, IMAP, dan POP3, lalu membahas faktor deliverability yang menentukan nasib pesan — semua sebelum kita menulis kode pengiriman pertama.

Perjalanan Sebuah Email #

Dua istilah fondasi perlu kita hafalkan lebih dulu. MUA (Mail User Agent) adalah program yang dipakai manusia untuk menulis dan membaca email, misalnya aplikasi Gmail, Outlook, Thunderbird, atau skrip PHP yang Anda tulis. MTA (Mail Transfer Agent) adalah perangkat lunak server yang bertugas menerima dan meneruskan pesan, misalnya Postfix, Exim, atau infrastruktur Gmail.

Perjalanan tipikal satu email terlihat seperti ini:

text
[MUA: skrip PHP Anda / aplikasi email pengirim]
        |  SMTP port 587 (STARTTLS + autentikasi)
        v
[MTA pengirim: server relay / SMTP provider]
        |  pencarian MX domain tujuan lewat DNS
        v
[MTA penerima: mx.example.com]
        |  pemeriksaan SPF, DKIM, DMARC + antispam
        v
[kotak masuk penerima --- dibaca lewat IMAP atau POP3]

Tahap pertama disebut submission: MUA menyerahkan pesan ke MTA pengirim melalui port 587 dengan enkripsi dan autentikasi. Selanjutnya MTA pengirim melakukan relay: mencari server tujuan lewat data MX pada DNS domain penerima, lalu berbicara SMTP antar server melalui port 25. Setibanya di MTA penerima, pesan diperiksa keasliannya; pesan yang mencurigakan ditolak, dikarantina ke folder spam, atau diterima ke kotak masuk.

SMTP, IMAP, dan POP3 Punya Tugas Berbeda #

Ketiga protokol ini sering dicampuradukkan pemula, padahal arah kerjanya berbeda. SMTP selalu berurusan dengan pengiriman, sedangkan IMAP dan POP3 berurusan dengan pembacaan. Tabel berikut merangkum pembagian tugasnya.

ProtokolArah kerjaTugas utamaPort umum
SMTPkeluar (mengirim)menyerahkan pesan dari MUA ke MTA dan antar MTA587 (submission), 25 (relay)
IMAPmasuk (membaca)menyinkronkan folder dan status pesan di server993 (TLS)
POP3masuk (membaca)mengunduh pesan lalu menghapusnya dari server995 (TLS)

Aplikasi PHP pada umumnya hanya membutuhkan SMTP, karena kebutuhan umum aplikasi adalah mengirim — notifikasi, verifikasi, reset password. IMAP dan POP3 baru relevan ketika Anda membangun aplikasi pembaca email seperti webmail atau alat pengarsipan pesan.

Mengapa Aplikasi Mengirim Lewat Relay atau API #

Secara teknis skrip PHP bisa berbicara langsung ke MTA tujuan melalui port 25. Praktik tersebut hampir selalu berakhir buruk: alamat IP server cloud dicurigai, reverse DNS sering tidak rapi, dan IP Anda belum punya reputasi pengiriman, sehingga pesan berakhir di spam atau ditolak. Karena itu aplikasi produksi menyerahkan email ke SMTP relay pihak ketiga — misalnya SendGrid, Mailgun, Amazon SES, atau Postmark — melalui koneksi SMTP terautentikasi, atau memakai API HTTP milik layanan tersebut. Relay inilah yang mengurus reputasi IP, pengiriman ulang, dan laporan bounce, sementara kode Anda cukup fokus menyusun pesan.

Faktor Deliverability: SPF, DKIM, dan DMARC #

Deliverability adalah peluang pesan Anda berakhir di kotak masuk, bukan di spam. Tiga mekanisme utamanya dikonfigurasi pada DNS domain pengirim, bukan di dalam kode PHP — tetapi Anda wajib memahaminya agar bisa menyiapkan domain dan memilih alamat From yang tepat.

MekanismePertanyaan yang dijawabTempat dikonfigurasikan
SPFAlamat IP mana yang berhak mengirim atas nama domain ini?record DNS TXT
DKIMApakah isi pesan tidak dimodifikasi di tengah jalan?tanda tangan digital di header + kunci publik di DNS
DMARCApa yang dilakukan penerima bila SPF/DKIM gagal?record DNS TXT

SPF mendaftarkan IP server yang sah untuk sebuah domain. DKIM menandatangani pesan secara kriptografi sehingga penerima bisa memverifikasi bahwa pesan tidak diubah sejak dikirim. DMARC menyatakan kebijakan bila kedua pemeriksaan itu gagal — diam saja, karantina, atau tolak — sekaligus meminta laporan ke pemilik domain. Ketika Anda memakai relay pihak ketiga, dokumentasi layanan tersebut selalu menyertakan record DNS yang perlu Anda pasang agar email tetap sah.

Email Transaksional vs Email Marketing #

Email yang dikirim aplikasi terbagi dua kategori besar dengan ekspektasi berbeda. Email transaksional dipicu aksi satu pengguna — kode verifikasi, tautan reset password, nota pesanan — sehingga harus terkirim segera dan personal. Email marketing seperti newsletter atau promosi dikirim massal ke ribuan penerima, mengutamakan pengelolaan daftar penerima, tautan berhenti berlangganan, dan pembatasan laju kirim agar tidak dianggap spam. Kode PHP pada umumnya menangani email transaksional satu per satu; pengiriman massal sebaiknya diserahkan ke layanan yang memang dirancang untuk itu.

Kenyataan di Lingkungan Pengembangan #

Mengirim email nyata dari komputer pengembangan adalah ide buruk: Anda bisa mengirimi pelanggan sungguhan dari data uji, atau membanjiri alamat produksi. Praktik yang benar adalah menampung pesan tanpa meneruskannya. Tiga pendekatan umum: pertama, mode log — pesan hanya ditulis ke berkas catatan; kedua, SMTP catcher lokal seperti MailHog yang menerima SMTP di port 1025 dan menampilkannya di antarmuka web di port 8025; ketiga, layanan sandbox seperti Mailtrap yang menyediakan kredensial SMTP khusus pengembangan dan menampung semua pesan di inbox web. Ketiganya membuat alur kode pengiriman tetap bisa diuji tanpa email keluar dari mesin Anda.

Validasi Alamat Email Sebelum Mengirim #

Sebelum menyerahkan pesan ke SMTP, langkah termurah untuk menjaga kualitas pengiriman adalah memvalidasi alamat penerima di sisi kode. PHP menyediakan filter bawaan FILTER_VALIDATE_EMAIL yang memeriksa format alamat. Mari kita uji beberapa contoh:

php
<?php
$daftarAlamat = [
    "[email protected]",
    "siti@@example.com",
    "budi@example",
    "[email protected]",
    "toko.example.com",
];

foreach ($daftarAlamat as $alamat) {
    $status = filter_var($alamat, FILTER_VALIDATE_EMAIL) ? "valid" : "tidak valid";
    echo $alamat . " => " . $status . "\n";
}

Output yang Dihasilkan:

text
[email protected] => valid
siti@@example.com => tidak valid
budi@example => tidak valid
[email protected] => valid
toko.example.com => tidak valid

Mari kita telaah kode di atas:

  1. Baris pertama ($daftarAlamat = [...]): array berisi lima alamat uji, dua di antaranya cacat secara sengaja.
  2. Baris filter_var($alamat, FILTER_VALIDATE_EMAIL): fungsi mengembalikan string alamat bila formatnya valid, atau false bila tidak; kita memakainya sebagai kondisi boolean.
  3. Baris echo ...: hasil perbandingan dicetak agar pola validasi mudah dibaca kolom per kolom.

Perhatikan dua hal dari output: budi@example ditolak karena domain tanpa titik tidak dianggap valid oleh filter ini, dan toko.example.com ditolak karena tidak memiliki tanda @.

Masukan pengguna sering datang dengan spasi berlebih dan kapitalisasi acak. Normalisasi kecil berikut membuat validasi lebih andal:

php
<?php
function bersihkanAlamat(string $alamat): string
{
    return strtolower(trim($alamat));
}

$masukan = "  [email protected]  ";
$bersih = bersihkanAlamat($masukan);

echo "Masukan mentah: [$masukan]\n";
echo "Setelah dibersihkan: [$bersih]\n";
echo "Valid menurut filter_var: " . (filter_var($bersih, FILTER_VALIDATE_EMAIL) ? "ya" : "tidak") . "\n";

Output yang Dihasilkan:

text
Masukan mentah: [  [email protected]  ]
Setelah dibersihkan: [[email protected]]
Valid menurut filter_var: ya

Fungsi bersihkanAlamat() memangkas spasi di awal dan akhir dengan trim(), lalu menyeragamkan huruf kecil dengan strtolower(). Bagian lokal sebuah alamat email secara teknis peka kapitalisasi, tetapi hampir semua penyedia memperlakukan alamat tanpa membedakan huruf besar-kecil, sehingga normalisasi ini aman dipraktikkan.

Sebagai rangkuman, berikut fungsi pemeriksaan yang menggabungkan normalisasi, validasi format, dan heuristik tambahan:

php
<?php
function periksaAlamat(string $alamat): array
{
    $galat = [];
    $alamat = strtolower(trim($alamat));

    if ($alamat === "") {
        $galat[] = "alamat kosong";
    } elseif (!filter_var($alamat, FILTER_VALIDATE_EMAIL)) {
        $galat[] = "format tidak valid";
    } else {
        $domain = substr(strrchr($alamat, "@"), 1);
        if (str_ends_with($domain, ".example")) {
            $galat[] = "domain khusus contoh, tidak menerima email nyata";
        }
    }

    return $galat;
}

$uji = ["  [email protected] ", "siti@@example.com", "[email protected]"];

foreach ($uji as $alamat) {
    $galat = periksaAlamat($alamat);
    echo "[$alamat] => " . (count($galat) === 0 ? "OK" : implode("; ", $galat)) . "\n";
}

Output yang Dihasilkan:

text
[  [email protected] ] => OK
[siti@@example.com] => format tidak valid
[[email protected]] => domain khusus contoh, tidak menerima email nyata

Alur fungsinya berjenjang: alamat kosong dilaporkan lebih dulu, alamat berformat salah dilaporkan berikutnya, dan alamat valid format diperiksa satu langkah lagi terhadap domain .example yang memang disediakan spesifikasi hanya untuk dokumentasi sehingga tidak pernah menerima email sungguhan. Pola berjenjang seperti ini mudah diperluas, misalnya dengan daftar domain sekali-pakai yang sering dipakai pendaftar palsu.

Ringkasan #

Email menempuh perjalanan MUA → MTA pengirim → MTA penerima → kotak masuk, dengan SMTP sebagai protokol pengirim dan IMAP/POP3 sebagai protokol pembaca. Deliverability ditentukan SPF, DKIM, dan DMARC di sisi DNS, dan aplikasi produksi sebaiknya mengirim melalui relay atau API tepercaya. Di lingkungan pengembangan, pesan ditampung lewat log, MailHog, atau sandbox seperti Mailtrap. Dengan peta ini di tangan, kita siap menulis kode: chapter berikutnya membahas pengiriman dengan mail() dan percakapan SMTP yang sebenarnya.

Referensi #