Mengamankan lalu lintas Swoole dengan TLS/SSL: konfigurasi sertifikat di Http dan WebSocket server, enable HTTPS dan WSS, redirect HTTP ke HTTPS, hingga pengamanan cipher dan mutual TLS.

Setelah di episode 17 kita mengamankan server dari serangan koneksi — pada episode kali ini kita mengamankan datanya. TLS/SSL adalah standar minimum di internet modern: tanpa HTTPS, data user (password, token, konten chat WebSocket) bisa dibaca siapa pun di jalur jaringan.
Mengapa episode ini penting? Karena Swoole sering dipakai tanpa reverse proxy — dan di situlah letak kesalahannya. Banyak tim membiarkan Swoole plaintext di balik Nginx dan tidak pernah belajar konfigurasi TLS di Swoole. Padahal Swoole bisa menangani HTTPS dan WSS secara native, dan kalian harus tahu caranya bila ingin standalone.
Pertama, pastikan Swoole dibangun dengan dukungan SSL:
php --ri swoole | grep -i sslHarus menunjukkan swoole.enable-openssl => On. Bila tidak, install ulang dengan --enable-openssl (episode 3).
Kedua, kalian butuh sertifikat. Untuk production gunakan Let's Encrypt:
sudo certbot certonly --standalone -d api.example.comFile yang dipakai Swoole:
fullchain.pem → sertifikat + chain (sertifikat ssl_cert_file)privkey.pem → private key (sertifikat ssl_key_file)Konfigurasi dasar HTTPS: listen dengan flag SWOOLE_SSL dan berikan path sertifikat:
<?php
use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;
$server = new Server('0.0.0.0', 443, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
$server->set([
'ssl_cert_file' => '/etc/letsencrypt/live/api.example.com/fullchain.pem',
'ssl_key_file' => '/etc/letsencrypt/live/api.example.com/privkey.pem',
'ssl_protocols' => SWOOLE_SSL_TLSV1_2 | SWOOLE_SSL_TLSV1_3,
]);
$server->on('Request', function (Request $req, Response $res) {
$res->end('Koneksi aman: ' . ($req->server['https'] ?? 'no'));
});
$server->start();Uji dengan curl:
curl -v https://127.0.0.1:443/ --resolve api.example.com:443:127.0.0.1Perhatikan $req->server['https'] — Swoole menandai koneksi SSL di sini; berguna untuk log dan logika redirect.
Pola standar: satu server di port 80 (plaintext) yang me-redirect semua request ke HTTPS:
<?php
use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;
$https = new Server('0.0.0.0', 443, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
$https->set([
'ssl_cert_file' => '/etc/letsencrypt/live/api.example.com/fullchain.pem',
'ssl_key_file' => '/etc/letsencrypt/live/api.example.com/privkey.pem',
]);
$https->on('Request', function (Request $req, Response $res) {
$res->end('Selamat datang di HTTPS!');
});
// Listener tambahan di port 80 — hanya redirect
$http = $https->addListener('0.0.0.0', 80, SWOOLE_SOCK_TCP);
// tangani port 80 lewat flag listener
$https->on('Request', function (Request $req, Response $res) use ($https) {
$isPlain = ($req->server['server_port'] ?? 443) === 80;
if ($isPlain) {
$host = $req->header['host'] ?? 'localhost';
$uri = $req->server['request_uri'] ?? '/';
$res->redirect("https://$host$uri", 301);
return;
}
$res->end('Selamat datang di HTTPS!');
});
$https->start();Tip
Dua on('Request') di atas sebenarnya akan saling menimpa — cara yang benar adalah satu handler dengan cek port (contoh kedua). Pola satu listener 443 + satu listener 80 dengan if ($server_port === 80) redirect adalah yang paling umum di produksi. SEO juga lebih baik dengan redirect 301 permanen.
WebSocket pakai koneksi yang sama — tinggal nyalakan SSL di listener dan client memakai wss://:
<?php
use Swoole\WebSocket\Server;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
$server = new Server('0.0.0.0', 9503, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
$server->set([
'ssl_cert_file' => '/etc/letsencrypt/live/chat.example.com/fullchain.pem',
'ssl_key_file' => '/etc/letsencrypt/live/chat.example.com/privkey.pem',
'ssl_protocols' => SWOOLE_SSL_TLSV1_2 | SWOOLE_SSL_TLSV1_3,
]);
$server->on('Open', function (Server $server, Request $req) {
$server->push($req->fd, 'Terhubung aman (WSS)');
});
$server->on('Message', function (Server $server, Frame $frame) {
$server->push($frame->fd, 'Echo: ' . $frame->data);
});
$server->start();Client di browser kini menghubungi wss://chat.example.com:9503/ — browser menolak WebSocket plaintext dari halaman HTTPS (mixed content), jadi WSS adalah keharusan.
Beberapa setelan tambahan yang menaikkan keamanan koneksi:
| Setelan | Fungsi |
|---|---|
ssl_protocols | Hanya izinkan TLS 1.2/1.3 — matikan TLS 1.0/1.1 yang sudah usang |
ssl_verify_peer | Verifikasi sertifikat client (untuk mutual TLS) |
ssl_ciphers | Daftar cipher suite yang diizinkan |
ssl_cert_file fullchain | Sertifikat + chain sekaligus — hindari "incomplete chain" |
Contoh mutual TLS (client juga perlu sertifikat):
$server->set([
'ssl_verify_peer' => true,
'ssl_verify_depth' => 3,
'ssl_verify_peer' => true,
'ssl_client_cert_file' => '/etc/ssl/ca.crt', // CA untuk memverifikasi client
]);| Masalah | Penyebab | Solusi |
|---|---|---|
bad PEM file | Path/format sertifikat salah | Pastikan fullchain + privkey; cek izin baca file |
| Browser "incomplete chain" | Hanya sertifikat leaf, bukan fullchain | Gunakan fullchain.pem, bukan cert.pem |
| TLS 1.0 masih bisa konek | ssl_protocols tidak di-set | Set hanya `SWOOLE_SSL_TLSV1_2 |
| Sertifikat expired tidak ketahuan | Tidak ada pemantauan | Cron cek expiry + alert (bisa pakai Timer, episode 10) |
wss:// gagal tapi https:// jalan | Port/SSL di listener salah | Cek flag SWOOLE_SSL di listener WebSocket |
Pada episode 18 ini, kalian telah mengamankan lalu lintas Swoole dengan TLS.
Inti yang harus dibawa pulang:
--enable-openssl; cek dengan php --ri swoole.SWOOLE_SSL + ssl_cert_file/ssl_key_file.server_port.wss://.ssl_protocols TLS 1.2/1.3, ssl_verify_peer untuk mTLS, dan fullchain certificate.Di episode 19 selanjutnya, kita mengamati server dari luar: Monitoring, Logging & Debugging — stats, event loop metrics, Xdebug di coroutine, dan export ke Prometheus/OpenTelemetry. Sampai jumpa di episode 19!