Cara Membuat Penyingkat URL Sendiri dengan Shlink
Bangun penyingkat URL di VPS dengan Shlink 5.1 dan Docker Compose, termasuk DNS domain pendek, Postgres, API key, web client 4.8, QR, dan statistik klik.
Yang akan Anda bangun
Penyingkat URL yang di-host sendiri adalah server kecil yang mengubah tautan panjang menjadi tautan pendek milik Anda dan menghitung setiap klik di dalamnya. Shlink adalah pilihan yang tepat: perangkat lunak ini bersifat sumber terbuka, tersedia sebagai image Docker, dan menjalankan seluruh fungsi tersebut dalam satu container serta sebuah database. Panduan ini memasangnya di VPS di balik domain pendek khusus, dengan HTTPS, API key, kode QR, dan statistik klik.
Dua komponen membuatnya berfungsi seperti penyingkat URL komersial. Server API menangani pengalihan dan menyimpan data. Web client adalah aplikasi statis terpisah yang berkomunikasi dengan API tersebut dari browser Anda. Anda dapat menjalankan keduanya, atau hanya menjalankan API dan mengoperasikannya dari command line.
Nomor versi di sini adalah versi yang berlaku pada Juli 2026: Shlink 5.1 dan shlink-web-client 4.8.
Arahkan domain pendek ke server terlebih dahulu
Domain adalah bagian utama layanan. s.example.com/abc123 adalah tautan yang dilihat orang, jadi pilih nama yang pendek dan tentukan sebelum memasang apa pun. Shlink menyimpan domain tersebut bersama setiap URL pendek. Jika domain diubah nanti, semua tautan yang sudah Anda bagikan akan berhenti berfungsi.
Buat satu data DNS A untuk domain pendek tersebut dan arahkan ke alamat IPv4 publik VPS Anda. Tambahkan juga data AAAA jika server memiliki IPv6. Kemudian, pastikan domain tersebut dapat di-resolve sebelum melanjutkan.
dig +short s.example.com AOutput harus berupa alamat server Anda. Jika kosong, data tersebut belum selesai dipropagasikan. Semua langkah berikutnya akan gagal dengan cara yang membingungkan karena sertifikat TLS (transport layer security) tidak dapat diterbitkan untuk nama yang tidak dapat di-resolve.
File compose
Shlink memerlukan database. SQLite dapat digunakan untuk pengujian, tetapi Postgres adalah pilihan yang tepat untuk apa pun yang ingin Anda pertahankan, karena baris kunjungan akan terus bertambah dan Postgres menangani indeks serta penulisan secara bersamaan dengan lebih baik. Simpan konfigurasi berikut di /opt/shlink/compose.yaml.
services:
shlink:
image: shlinkio/shlink:stable
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
environment:
DEFAULT_DOMAIN: s.example.com
IS_HTTPS_ENABLED: "true"
DB_DRIVER: postgres
DB_HOST: database
DB_NAME: shlink
DB_USER: shlink
DB_PASSWORD: ${DB_PASSWORD}
depends_on:
- database
database:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_DB: shlink
POSTGRES_USER: shlink
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- shlink_db:/var/lib/postgresql/data
web-client:
image: shlinkio/shlink-web-client:stable
restart: unless-stopped
ports:
- "127.0.0.1:8081:8080"
volumes:
shlink_db:Kedua port yang dipublikasikan terikat ke 127.0.0.1, sehingga tidak ada yang dapat diakses dari internet sampai reverse proxy pada bagian berikutnya aktif. Docker menulis aturan penerusan miliknya sendiri sebelum firewall host, sehingga baris 8080:8080 biasa akan mengekspos aplikasi, meskipun firewall pada server tampak tertutup. Mengikatnya ke alamat loopback mencegah hal tersebut. Pola yang sama berlaku untuk aplikasi apa pun yang Anda jalankan dengan cara ini, dan dibahas lebih lanjut dalam panduan Docker Compose pada VPS.
Kata sandi database berasal dari file .env di sebelah file compose, sehingga tidak pernah tersimpan dalam YAML.
sudo mkdir -p /opt/shlink
printf 'DB_PASSWORD=%s\n' "$(openssl rand -base64 24)" | sudo tee /opt/shlink/.env
sudo chmod 600 /opt/shlink/.envJalankan dan pantau hingga API aktif.
cd /opt/shlink
sudo docker compose up -d
sudo docker compose logs -f shlinkSaat pertama kali dijalankan, proses ini menjalankan migrasi database, sehingga memerlukan waktu lebih lama daripada proses berikutnya. Setelah selesai, periksa apakah layanan merespons secara lokal.
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/rest/health200 berarti API aktif dan koneksi database berfungsi. 500 di sini hampir selalu disebabkan oleh database: DB_PASSWORD dalam .env tidak cocok dengan nilai yang digunakan saat Postgres dibuat, karena image Postgres hanya membaca POSTGRES_PASSWORD ketika menginisialisasi direktori data kosong. Mengubah kata sandi setelahnya tidak berpengaruh sampai Anda menghapus volume dan menjalankannya kembali.
Terminasi HTTPS di depannya
Shlink menyediakan HTTP biasa pada port 8080. TLS harus ditangani oleh reverse proxy, dan satu pengaturan yang penting adalah meneruskan nama host asli. Shlink menentukan domain tempat suatu kode singkat berada dengan membaca header Host. Karena itu, proxy yang menulis ulang header tersebut akan menghasilkan respons 404 pada tautan yang sebenarnya ada, serta mencatat statistik kunjungan pada domain yang salah.
server {
server_name s.example.com;
listen 80;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Kemudian terbitkan sertifikatnya. Panduan lengkapnya, termasuk timer pembaruan, tersedia dalam panduan Certbot untuk nginx di Ubuntu 24.04.
sudo certbot --nginx -d s.example.comIS_HTTPS_ENABLED: "true" dalam file compose membuat Shlink mencetak https:// pada URL singkat yang dikembalikannya. Pengaturan ini tidak mengaktifkan TLS dengan sendirinya. Biarkan nilainya false di belakang proxy HTTPS. Dengan demikian, setiap tautan yang dikembalikan API berupa tautan http:// yang kemudian dialihkan, sehingga menambah satu perjalanan pulang-pergi dan tampak keliru di klien web.
Buat kunci API
API tidak dapat menerima komunikasi tanpa kunci. Buat kunci melalui CLI di dalam container.
sudo docker compose exec shlink shlink api-key:generate --name "web client"Perintah tersebut hanya menampilkan kunci satu kali. Salin sekarang, karena kunci disimpan dalam bentuk hash dan tidak dapat ditampilkan lagi. shlink api-key:list menampilkan nama dan status aktif setiap kunci, tetapi tidak pernah menampilkan kuncinya. Cabut kunci dengan shlink api-key:disable dan namanya.
Setiap panggilan REST menyertakan kunci dalam header X-Api-Key.
curl -H "X-Api-Key: YOUR_KEY" https://s.example.com/rest/v3/short-urlsObjek JSON dengan kunci shortUrls berarti kunci berfungsi. 401 yang memuat INVALID_API_KEY berarti kunci salah, dinonaktifkan, atau sudah melewati tanggal kedaluwarsanya.
Membuat tautan singkat dari baris perintah
CLI adalah cara tercepat untuk membuat tautan dan dapat digunakan dengan baik oleh skrip.
sudo docker compose exec shlink shlink short-url:create https://example.com/a/very/long/path
sudo docker compose exec shlink shlink short-url:create https://example.com/docs --custom-slug docs --tag reference--custom-slug memberi Anda tautan yang mudah dibaca, bukan kode yang dibuat secara otomatis. Slug bersifat unik untuk setiap domain. Karena itu, percobaan kedua menggunakan slug yang sudah dipakai akan gagal, bukan menimpa tautan pertama secara diam-diam. --tag dapat diulang. Tag digunakan untuk mengelompokkan tautan yang nantinya ingin Anda lihat statistik gabungannya.
Tampilkan tautan yang ada, lalu periksa traffic salah satu tautan.
sudo docker compose exec shlink shlink short-url:list
sudo docker compose exec shlink shlink short-url:visits docsshort-url:visits menampilkan satu baris untuk setiap klik, lengkap dengan tanggal, referrer, dan user agent. Kolom negara dan kota tetap kosong kecuali Anda menetapkan variabel lingkungan GEOLITE_LICENSE_KEY. Variabel ini berisi key MaxMind gratis yang digunakan Shlink untuk mengunduh database GeoLite2. Tanpa variabel tersebut, kunjungan tetap dicatat, tetapi lokasinya tidak ditentukan.
Klien web dan kode QR
Klien web kini tersedia di 127.0.0.1:8081 dan memerlukan entri proxy sendiri, atau tunnel SSH jika Anda tidak ingin memublikasikannya. Pada pemuatan pertama, klien meminta URL server dan kunci API. Masukkan https://s.example.com dan kunci yang Anda buat. Klien menyimpan keduanya di penyimpanan browser dan memanggil API Anda secara langsung, sehingga tidak ada data yang melewati pihak lain.
Kode QR tidak memerlukan konfigurasi sama sekali. Tambahkan /qr-code ke URL pendek apa pun, lalu API mengembalikan gambarnya.
https://s.example.com/docs/qr-code?size=500&format=svg&margin=20size adalah lebar dalam piksel dan menerima nilai 50 hingga 1000, dengan nilai default 300. format adalah png atau svg. margin adalah ruang kosong di sekitar kode dalam piksel, dan ukuran gambar akhir adalah ukuran tersebut ditambah dua kali margin. Tambahkan errorCorrection=Q untuk kode yang tetap dapat dipindai saat dicetak dalam ukuran kecil atau tertutup sebagian.
Pastikan Tetap Berjalan
Layanan pemendek URL dapat gagal tanpa terlihat. Tautan berhenti melakukan pengalihan dan tidak ada yang memberi tahu Anda karena orang yang mengekliknya mengira tautan tersebut sudah tidak berfungsi. Arahkan pemeriksaan uptime ke URL pendek yang sebenarnya, bukan ke halaman beranda, lalu buat peringatan untuk apa pun yang bukan pengalihan. Instance Uptime Kuma yang di-host sendiri dapat melakukan hal ini dengan baik dan dapat memantau kode status tertentu.
Cadangkan database, bukan container. Satu perintah dapat mencadangkannya.
sudo docker compose exec -T database pg_dump -U shlink shlink | gzip > shlink-$(date +%F).sql.gzFile tersebut bersama file compose dapat membangun ulang seluruh layanan di server baru. Proses upgrade dilakukan dengan sudo docker compose pull, kemudian sudo docker compose up -d, dan Shlink menjalankan migrasi baru saat memulai. Buat cadangan sebelum menjalankan pull karena migrasi tidak dapat dibatalkan.
FAQ
Mengapa tautan singkat saya mengembalikan 404 setelah saya menambahkan reverse proxy?
Shlink mencocokkan kode singkat dengan domain dalam header Host. Proxy yang mengirimkan namanya sendiri atau alamat internal membuat Shlink mencari kode tersebut pada domain yang tidak memiliki tautan. Akibatnya, Shlink mengembalikan 404. Tetapkan proxy_set_header Host $host; dalam blok lokasi nginx, lalu muat ulang proxy. Tautan langsung berfungsi kembali tanpa memulai ulang container.
Apakah saya memerlukan Postgres, atau SQLite sudah cukup?
SQLite cukup untuk mencoba Shlink dan tidak memerlukan container kedua. Beralihlah ke Postgres sebelum Anda memublikasikan tautan penting, karena baris kunjungan bertambah setiap kali terjadi klik dan SQLite membuat operasi tulis berjalan secara serial. Jika beralih nanti, Anda harus mengekspor dan mengimpor ulang tautan. Memilih Postgres sejak awal menghindarkan Anda dari migrasi tersebut.
Apakah saya dapat memulihkan API key yang lupa saya salin?
Tidak. Shlink menyimpan hash key tersebut. Karena itu, api-key:list hanya menampilkan nama dan status, bukan nilainya. Buat pengganti dengan shlink api-key:generate, tempelkan ke klien web, lalu nonaktifkan key lama dengan shlink api-key:disable agar tidak dapat digunakan lagi.
Mengapa kolom negara kosong dalam statistik kunjungan saya?
Geolokasi memerlukan database GeoLite2. Shlink hanya mengunduhnya jika Anda memberikan GEOLITE_LICENSE_KEY. Key tersebut tersedia gratis dari MaxMind. Tambahkan key ke bagian environment, buat ulang container, dan kunjungan baru akan mendapatkan lokasi. Kunjungan yang dicatat sebelumnya tetap kosong sampai Anda menjalankan shlink visit:locate.
Bagaimana cara memindahkan Shlink ke server lain?
Pertahankan domain dan pindahkan datanya. Buat dump database dengan pg_dump, salin dump dan file compose ke server baru, jalankan stack, lalu pulihkan dump ke database kosong sebelum lalu lintas sebenarnya masuk. Ubah catatan DNS terakhir. Kode singkat dan riwayat kunjungannya tetap ada karena semuanya tersimpan dalam database.