Pemendek URL Sendiri dengan Shlink dan Docker
Bina pemendek URL sendiri pada VPS menggunakan Shlink 5.1 dan Docker Compose, lengkap dengan DNS, Postgres, kunci API, klien web, kod QR serta statistik klik.
Perkara yang anda bina
Pemendek URL yang dihoskan sendiri ialah pelayan kecil yang menukar pautan panjang menjadi pautan pendek milik anda dan mengira setiap klik padanya. Shlink ialah pilihan yang sesuai kerana ia sumber terbuka, dikeluarkan sebagai imej Docker, dan melaksanakan keseluruhan tugas dalam satu bekas serta pangkalan data. Panduan ini memasangnya pada VPS di sebalik domain pendek sebenar, dengan HTTPS, kunci API, kod QR dan statistik klik.
Dua komponen menjadikannya serupa dengan perkhidmatan pemendek URL komersial. Pelayan API mengendalikan ubah hala dan menyimpan data. Klien web ialah aplikasi statik berasingan yang berkomunikasi dengan API itu daripada pelayar anda. Anda boleh menjalankan kedua-duanya atau menjalankan API sahaja dan mengawalnya daripada baris perintah.
Nombor versi di sini ialah versi semasa pada Julai 2026: Shlink 5.1 dan shlink-web-client 4.8.
Halakan domain pendek ke pelayan terlebih dahulu
Domain ialah produk anda. s.example.com/abc123 ialah pautan yang dilihat orang, jadi pilih nama yang pendek dan tentukannya sebelum anda memasang apa-apa. Shlink menyimpan domain itu bersama setiap URL pendek. Jika anda menukarnya kemudian, semua pautan yang telah anda edarkan tidak lagi berfungsi.
Cipta satu rekod DNS A untuk domain pendek tersebut dan halakannya ke alamat IPv4 awam VPS anda. Tambahkan rekod AAAA jika pelayan mempunyai IPv6. Kemudian sahkan bahawa domain itu dapat diselesaikan sebelum anda meneruskan.
dig +short s.example.com AOutput mestilah alamat pelayan anda. Jika output kosong, rekod itu belum tersebar lagi. Setiap langkah seterusnya akan gagal dengan cara yang mengelirukan kerana sijil TLS (transport layer security) tidak boleh dikeluarkan untuk nama yang tidak dapat diselesaikan.
Fail compose
Shlink memerlukan pangkalan data. SQLite sesuai untuk ujian, tetapi Postgres ialah pilihan yang betul untuk apa-apa yang akan disimpan, kerana baris lawatan akan terus bertambah dan Postgres mengendalikan indeks serta penulisan serentak dengan lebih baik. Letakkan kandungan ini dalam /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-dua port yang diterbitkan diikat pada 127.0.0.1, jadi tiada apa-apa boleh dicapai dari Internet sehingga proksi terbalik dalam bahagian seterusnya disediakan. Docker menulis peraturan pemajuannya sendiri sebelum tembok api hos, yang bermaksud baris 8080:8080 biasa akan mendedahkan aplikasi walaupun tembok api pada pelayan kelihatan tertutup. Pengikatan pada alamat gelung balik mengelakkan perkara itu. Corak yang sama terpakai pada mana-mana aplikasi yang anda jalankan dengan cara ini, dan diterangkan dengan lebih terperinci dalam panduan Docker Compose pada VPS.
Kata laluan pangkalan data datang daripada fail .env di sebelah fail compose, jadi kata laluan itu tidak pernah dimasukkan ke 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/.envMulakannya dan pantau API sehingga tersedia.
cd /opt/shlink
sudo docker compose up -d
sudo docker compose logs -f shlinkPermulaan pertama menjalankan migrasi pangkalan data, jadi proses ini mengambil masa lebih lama berbanding permulaan seterusnya. Selepas proses selesai, semak bahawa perkhidmatan memberikan respons secara setempat.
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/rest/health200 bermaksud API aktif dan sambungan pangkalan data berfungsi. 500 di sini hampir selalu berpunca daripada pangkalan data: DB_PASSWORD dalam .env tidak sepadan dengan nilai yang digunakan semasa Postgres dicipta, kerana imej Postgres hanya membaca POSTGRES_PASSWORD apabila ia memulakan direktori data yang kosong. Mengedit kata laluan kemudian tidak memberikan kesan sehingga anda mengalih keluar volum dan memulakannya semula.
Tamatkan HTTPS di hadapannya
Shlink menyediakan HTTP biasa pada port 8080. TLS perlu dikendalikan oleh proksi songsang. Tetapan yang penting ialah meneruskan nama hos asal. Shlink menentukan domain yang dikaitkan dengan kod pendek dengan membaca pengepala Host. Oleh itu, proksi yang menulis semula pengepala tersebut akan menghasilkan respons 404 untuk pautan yang wujud. Statistik lawatan juga akan dikaitkan dengan 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 keluarkan sijil. Panduan lengkap, termasuk pemasa pembaharuan, terdapat dalam panduan Certbot untuk nginx pada Ubuntu 24.04.
sudo certbot --nginx -d s.example.comIS_HTTPS_ENABLED: "true" dalam fail compose menyebabkan Shlink mencetak https:// dalam URL pendek yang dikembalikannya. Tetapan itu sendiri tidak mendayakan TLS. Biarkan false di belakang proksi HTTPS. Dengan itu, setiap pautan yang dikembalikan oleh API ialah pautan http:// yang kemudiannya membuat ubah hala. Keadaan ini menambah satu perjalanan pergi balik dan kelihatan tidak betul dalam klien web.
Cipta kunci API
Tiada apa-apa boleh berkomunikasi dengan API tanpa kunci. Jana kunci melalui CLI di dalam kontena.
sudo docker compose exec shlink shlink api-key:generate --name "web client"Perintah ini memaparkan kunci sekali sahaja. Salin kunci itu sekarang kerana kunci disimpan dalam bentuk cincangan dan tidak boleh dipaparkan lagi. shlink api-key:list memaparkan nama serta status setiap kunci, tetapi tidak pernah memaparkan kunci itu sendiri. Batalkan kunci dengan shlink api-key:disable dan namanya.
Setiap panggilan REST membawa kunci dalam pengepala X-Api-Key.
curl -H "X-Api-Key: YOUR_KEY" https://s.example.com/rest/v3/short-urlsObjek JSON dengan kunci shortUrls bermakna kunci itu berfungsi. 401 yang mengandungi INVALID_API_KEY bermakna kunci itu salah, dilumpuhkan atau telah melepasi tarikh luput.
Cipta pautan pendek daripada baris perintah
CLI ialah cara terpantas untuk mencipta pautan dan sesuai digunakan dalam 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 pautan yang mudah dibaca dan bukannya kod yang dijana. Slug adalah unik bagi setiap domain, jadi percubaan kedua menggunakan slug yang telah diambil akan gagal dan tidak menulis ganti pautan pertama secara senyap. --tag boleh diulang, dan tag digunakan untuk mengumpulkan pautan yang statistik gabungannya ingin anda lihat kemudian.
Senaraikan perkara yang tersedia, kemudian lihat trafik bagi satu pautan.
sudo docker compose exec shlink shlink short-url:list
sudo docker compose exec shlink shlink short-url:visits docsshort-url:visits mencetak satu baris bagi setiap klik, termasuk tarikh, perujuk dan ejen pengguna. Lajur negara dan bandar kekal kosong melainkan anda menetapkan pemboleh ubah persekitaran GEOLITE_LICENSE_KEY, iaitu kunci MaxMind percuma yang digunakan oleh Shlink untuk memuat turun pangkalan data GeoLite2. Tanpanya, lawatan masih direkodkan, tetapi lokasinya tidak dikenal pasti.
Klien web dan kod QR
Klien web kini berada di 127.0.0.1:8081 dan memerlukan entri proksi sendiri, atau terowong SSH jika anda tidak mahu menerbitkannya. Pada pemuatan pertama, klien meminta URL pelayan dan kunci API. Masukkan https://s.example.com dan kunci yang anda jana. Klien menyimpan kedua-duanya dalam storan pelayar dan memanggil API anda secara terus, jadi tiada data melalui pihak lain.
Kod QR tidak memerlukan sebarang konfigurasi. Tambahkan /qr-code pada mana-mana URL pendek dan API akan mengembalikan imej.
https://s.example.com/docs/qr-code?size=500&format=svg&margin=20size ialah lebar dalam piksel dan menerima nilai 50 hingga 1000, dengan 300 sebagai nilai lalai. format ialah png atau svg. margin ialah ruang kosong di sekeliling kod dalam piksel, dan saiz imej siap ialah saiz tersebut ditambah dua kali margin. Tambahkan errorCorrection=Q untuk kod yang masih boleh diimbas apabila dicetak kecil atau dilitupi sebahagiannya.
Pastikan perkhidmatan terus berjalan
Pemendek URL boleh gagal tanpa disedari. Pautan berhenti membuat ubah hala dan tiada sesiapa memaklumkannya kerana orang yang mengklik menganggap pautan itu sudah tidak berfungsi. Halakan pemeriksaan masa aktif kepada URL pendek sebenar, bukan halaman utama, dan hantar amaran untuk sebarang respons yang bukan ubah hala. Instans Uptime Kuma yang dihoskan sendiri melaksanakan perkara ini dengan baik dan boleh memantau kod status tertentu.
Sandarkan pangkalan data, bukan kontena. Satu arahan akan membuang kandungan pangkalan data ke dalam fail.
sudo docker compose exec -T database pg_dump -U shlink shlink | gzip > shlink-$(date +%F).sql.gzFail itu bersama-sama fail compose anda boleh membina semula keseluruhan perkhidmatan pada pelayan baharu. Proses naik taraf ialah sudo docker compose pull diikuti oleh sudo docker compose up -d, dan Shlink menjalankan sebarang migrasi baharu semasa dimulakan. Ambil dump sebelum anda melakukan pull kerana migrasi tidak boleh dikembalikan.
FAQ
Mengapakah pautan pendek saya memulangkan 404 selepas saya menambah proksi terbalik?
Shlink memadankan kod pendek dengan domain dalam pengepala Host. Proksi yang menghantar namanya sendiri atau alamat dalaman menyebabkan Shlink mencari kod tersebut di bawah domain yang tiada pautan. Oleh itu, Shlink memulangkan 404. Tetapkan proxy_set_header Host $host; dalam blok lokasi nginx dan muat semula proksi. Pautan akan berfungsi serta-merta tanpa memulakan semula kontena.
Adakah saya memerlukan Postgres, atau adakah SQLite mencukupi?
SQLite sesuai untuk mencuba Shlink dan tidak memerlukan kontena kedua. Beralih kepada Postgres sebelum anda menerbitkan pautan penting kerana rekod lawatan bertambah dengan setiap klik, manakala SQLite mensiri operasi tulis. Pertukaran kemudian memerlukan anda mengeksport dan mengimport semula pautan. Oleh itu, memilih Postgres dari awal dapat mengelakkan migrasi tersebut.
Bolehkah saya mendapatkan semula kunci API yang terlupa saya salin?
Tidak. Shlink menyimpan cincangan kunci tersebut. Oleh itu, api-key:list hanya memaparkan nama dan status, bukan nilainya. Jana pengganti dengan shlink api-key:generate, tampalkannya ke dalam klien web, kemudian nyahdayakan kunci lama dengan shlink api-key:disable supaya kunci itu tidak lagi berfungsi.
Mengapakah lajur negara kosong dalam statistik lawatan saya?
Geolokasi memerlukan pangkalan data GeoLite2. Shlink hanya memuat turunnya apabila anda memberikan GEOLITE_LICENSE_KEY. Kunci itu percuma daripada MaxMind. Tambahkannya pada bahagian persekitaran, cipta semula kontena, dan lawatan baharu akan dikenal pasti lokasinya. Lawatan yang direkodkan sebelum itu kekal kosong sehingga anda menjalankan shlink visit:locate.
Bagaimanakah saya memindahkan Shlink ke pelayan lain?
Kekalkan domain dan pindahkan data. Buat dump pangkalan data dengan pg_dump, salin dump dan fail compose ke pelayan baharu, mulakan tindanan, kemudian pulihkan dump ke dalam pangkalan data kosong sebelum trafik sebenar tiba. Tukar rekod DNS pada langkah terakhir. Kod pendek dan sejarah lawatannya akan kekal kerana semuanya disimpan dalam pangkalan data.