Shlink দিয়ে নিজের URL শর্টেনার তৈরি করার নিয়ম
Docker Compose ব্যবহার করে কীভাবে নিজের VPS-এ Shlink সেটআপ করবেন তা জানুন। আমরা Postgres ডাটাবেস, API কি, HTTPS কনফিগারেশন এবং ক্লিক পরিসংখ্যান ট্র্যাক করার পূর্ণাঙ্গ পদ্ধতি দেখাব।
আপনি যা তৈরি করছেন
একটি সেলফ-হোস্টেড URL শর্টেনার হলো একটি ছোট সার্ভার, যা একটি দীর্ঘ লিঙ্ককে আপনার মালিকানাধীন একটি ছোট লিঙ্কে রূপান্তর করে এবং প্রতিটি ক্লিকের হিসাব রাখে। এক্ষেত্রে Shlink বেছে নেওয়া বুদ্ধিমানের কাজ: এটি ওপেন সোর্স, এটি একটি Docker ইমেজ হিসেবে রিলিজ হয় এবং এটি একটি কন্টেইনার ও একটি ডাটাবেসের মাধ্যমে পুরো কাজটি সম্পন্ন করে। এই নির্দেশিকাটি আপনাকে একটি VPS-এ একটি আসল শর্ট ডোমেইনের অধীনে, HTTPS, একটি API key, QR কোড এবং ক্লিক পরিসংখ্যানসহ এটি সেটআপ করতে সাহায্য করবে।
দুটি অংশ এটিকে একটি বাণিজ্যিক শর্টেনারের মতো কার্যকর করে তোলে। API সার্ভারটি রিডাইরেক্টের উত্তর দেয় এবং ডাটা সংরক্ষণ করে। ওয়েব ক্লায়েন্ট হলো একটি আলাদা স্ট্যাটিক অ্যাপ, যা আপনার ব্রাউজার থেকে সেই API-এর সাথে যোগাযোগ করে। আপনি চাইলে উভয়টি চালাতে পারেন, অথবা শুধুমাত্র API চালিয়ে কমান্ড লাইনের মাধ্যমে তা নিয়ন্ত্রণ করতে পারেন।
এখানে উল্লিখিত ভার্সন নম্বরগুলো জুলাই 2026 অনুযায়ী বর্তমান: Shlink 5.1 এবং shlink-web-client 4.8।
প্রথমে সার্ভারের দিকে একটি ছোট ডোমেইন নির্দেশ করুন
ডোমেইনটিই হলো পণ্য। s.example.com/abc123 হলো সেই লিঙ্ক যা মানুষ দেখতে পায়, তাই কিছু ইনস্টল করার আগেই ছোট এবং উপযুক্ত একটি ডোমেইন বেছে নিন। Shlink প্রতিটি ছোট URL-এর সাথে ডোমেইনটি সংরক্ষণ করে, এবং পরবর্তীতে এটি পরিবর্তন করলে আপনার বিতরণ করা সমস্ত লিঙ্ক কাজ করা বন্ধ করে দেবে।
আপনার VPS-এর পাবলিক IPv4 ঠিকানাকে নির্দেশ করে ছোট ডোমেইনটির জন্য একটি DNS A রেকর্ড তৈরি করুন। সার্ভারে IPv6 থাকলে একটি AAAA রেকর্ডও যোগ করুন। এরপর পরবর্তী ধাপে যাওয়ার আগে নিশ্চিত করুন যে এটি সঠিকভাবে রেজলভ (resolve) হচ্ছে।
dig +short s.example.com Aআউটপুট হিসেবে আপনার সার্ভারের ঠিকানা আসা উচিত। যদি এটি খালি থাকে, তবে রেকর্ডটি এখনও প্রোপাগেট (propagate) হয়নি। সেক্ষেত্রে পরবর্তী প্রতিটি ধাপ বিভ্রান্তিকরভাবে ব্যর্থ হবে, কারণ যে নাম রেজলভ হয় না তার জন্য কোনো TLS (transport layer security) সার্টিফিকেট ইস্যু করা সম্ভব নয়।
compose ফাইল
Shlink-এর একটি ডেটাবেস প্রয়োজন। পরীক্ষার জন্য SQLite কাজ করে, কিন্তু আপনি যদি কোনো কিছু দীর্ঘস্থায়ী করতে চান তবে Postgres সঠিক পছন্দ। কারণ ভিজিট রো (visit rows) জমা হতে থাকে এবং Postgres ইনডেক্স ও কনকারেন্ট রাইট (concurrent writes) আরও ভালোভাবে পরিচালনা করে। এটি /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:উভয় পাবলিশড পোর্ট 127.0.0.1-এ বাইন্ড (bind) করা থাকে, তাই পরবর্তী সেকশনের রিভার্স প্রক্সি সেটআপ না করা পর্যন্ত ইন্টারনেট থেকে কোনো কিছুই অ্যাক্সেস করা যাবে না। Docker হোস্ট ফায়ারওয়ালের আগেই নিজস্ব ফরোয়ার্ডিং রুল লেখে। এর মানে হলো, একটি সাধারণ 8080:8080 লাইন এমন সার্ভারেও অ্যাপটিকে উন্মুক্ত করে দেবে যার ফায়ারওয়াল বন্ধ বলে মনে হয়। লুপব্যাক অ্যাড্রেসে বাইন্ড করলে এই সমস্যা এড়ানো যায়। আপনি এই পদ্ধতিতে যে অ্যাপই চালান না কেন, একই প্যাটার্ন প্রযোজ্য। এ বিষয়ে বিস্তারিত Docker Compose অন VPS গাইড-এ আলোচনা করা হয়েছে।
ডেটাবেস পাসওয়ার্ডটি compose ফাইলের পাশে থাকা একটি .env ফাইল থেকে আসে, তাই এটি কখনোই 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/.envএটি চালু করুন এবং API সচল হচ্ছে কি না তা পর্যবেক্ষণ করুন।
cd /opt/shlink
sudo docker compose up -d
sudo docker compose logs -f shlinkপ্রথমবার চালু করার সময় ডেটাবেস মাইগ্রেশন সম্পন্ন হয়, তাই পরবর্তী সময়ের তুলনায় এতে বেশি সময় লাগে। যখন এটি স্থিতিশীল হবে, তখন চেক করুন যে সার্ভিসটি লোকালি রেসপন্স করছে কি না।
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/rest/healthএকটি 200 মানে হলো API সচল আছে এবং ডেটাবেস কানেকশন কাজ করছে। এখানে একটি 500 আসার অর্থ প্রায় সবসময়ই ডেটাবেস সংক্রান্ত সমস্যা: .env-এর মধ্যে থাকা DB_PASSWORD-এর সাথে Postgres তৈরির সময়কার পাসওয়ার্ডের মিল নেই। কারণ Postgres ইমেজ শুধুমাত্র তখনই POSTGRES_PASSWORD পড়ে যখন এটি একটি খালি ডেটা ডিরেক্টরি ইনিশিয়ালাইজ করে। পরবর্তীতে পাসওয়ার্ড পরিবর্তন করলে কোনো কাজ হবে না যতক্ষণ না আপনি ভলিউমটি রিমুভ করে পুনরায় শুরু করছেন।
HTTPS টার্মিনেশন কনফিগার করুন
Shlink পোর্ট 8080-এ সাধারণ HTTP পরিবেশন করে। TLS-এর কাজ একটি রিভার্স প্রক্সির মাধ্যমে সম্পন্ন হওয়া উচিত এবং এক্ষেত্রে সবচেয়ে গুরুত্বপূর্ণ সেটিংস হলো মূল হোস্ট নেমটি সঠিকভাবে পাস করা। Shlink কোন ডোমেইনের অধীনে একটি শর্ট কোড রয়েছে তা নির্ধারণ করে Host হেডারটি পড়ার মাধ্যমে। তাই কোনো প্রক্সি যদি এই হেডারটি পরিবর্তন করে দেয়, তবে বিদ্যমান লিঙ্কগুলোতে 404 রেসপন্স আসবে এবং ভিজিট স্ট্যাটাসগুলো ভুল ডোমেইনের সাথে যুক্ত হবে।
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;
}
}এরপর সার্টিফিকেট ইস্যু করুন। রিনিউয়াল টাইমারসহ সম্পূর্ণ নির্দেশিকাটি Ubuntu 24.04-এ nginx-এর জন্য Certbot গাইড-এ পাওয়া যাবে।
sudo certbot --nginx -d s.example.comcompose ফাইলে থাকা IS_HTTPS_ENABLED: "true" মূলত Shlink-কে তার রিটার্ন করা শর্ট URL-গুলোতে https:// প্রিন্ট করতে নির্দেশ দেয়। এটি নিজে থেকে TLS সক্রিয় করে না। এটিকে একটি HTTPS প্রক্সির পেছনে false হিসেবে রেখে দিলে API থেকে প্রাপ্ত প্রতিটি লিঙ্ক একটি http:// লিঙ্ক হয়ে যায়, যা পরবর্তীতে রিডাইরেক্ট হয়। এতে অতিরিক্ত রাউন্ড ট্রিপ সময় ব্যয় হয় এবং ওয়েব ক্লায়েন্টে তা ভুল দেখায়।
API কি তৈরি করা
একটি কি (key) ছাড়া কোনো কিছুই API-এর সাথে যোগাযোগ করতে পারে না। কন্টেইনারের ভেতরে CLI ব্যবহার করে একটি কি তৈরি করুন।
sudo docker compose exec shlink shlink api-key:generate --name "web client"এই কমান্ডটি একবারই কি-টি প্রদর্শন করে। এখনই এটি কপি করে নিন, কারণ এটি হ্যাশ (hashed) অবস্থায় সংরক্ষিত থাকে এবং পরবর্তীতে আর দেখানো সম্ভব নয়। shlink api-key:list কমান্ডটি কি-এর নাম এবং প্রতিটি কি সক্রিয় কি না তা দেখায়, কিন্তু কখনোই কি-টি নিজে দেখায় না। shlink api-key:disable এবং কি-এর নাম ব্যবহার করে একটি কি বাতিল (revoke) করা যায়।
প্রতিটি REST কলে X-Api-Key হেডারে এই কি-টি বহন করতে হয়।
curl -H "X-Api-Key: YOUR_KEY" https://s.example.com/rest/v3/short-urlsএকটি JSON অবজেক্টে shortUrls কি থাকা মানে হলো কি-টি সঠিকভাবে কাজ করছে। 401 এর সাথে INVALID_API_KEY বার্তাটি আসার অর্থ হলো কি-টি ভুল, নিষ্ক্রিয়, অথবা এর মেয়াদ উত্তীর্ণ হয়ে গেছে।
কমান্ড লাইন থেকে শর্ট লিঙ্ক তৈরি করা
লিঙ্ক তৈরি করার জন্য CLI হলো দ্রুততম উপায় এবং এটি স্ক্রিপ্টিংয়ের জন্য অত্যন্ত কার্যকর।
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 ব্যবহার করলে আপনি জেনারেট করা কোডের পরিবর্তে একটি পাঠযোগ্য লিঙ্ক পাবেন। প্রতিটি ডোমেইনের জন্য স্লাগগুলো অনন্য হয়, তাই কোনো স্লাগ আগে থেকেই নেওয়া থাকলে দ্বিতীয়বার চেষ্টা করলে তা ব্যর্থ হবে; এটি আগের লিঙ্কটিকে নীরবে ওভাররাইট করবে না। --tag বারবার ব্যবহার করা যায় এবং ট্যাগ ব্যবহার করে আপনি সেই লিঙ্কগুলোকে গ্রুপ করতে পারেন যেগুলোর সম্মিলিত পরিসংখ্যান আপনি পরে দেখতে চান।
বিদ্যমান লিঙ্কগুলোর তালিকা দেখুন এবং তারপর একটি লিঙ্কের ট্রাফিক পর্যবেক্ষণ করুন।
sudo docker compose exec shlink shlink short-url:list
sudo docker compose exec shlink shlink short-url:visits docsshort-url:visits প্রতিটি ক্লিকের জন্য একটি করে সারি প্রিন্ট করে, যেখানে তারিখ, রেফারার এবং ইউজার এজেন্ট উল্লেখ থাকে। দেশ এবং শহরের কলামগুলো খালি থাকে যদি না আপনি GEOLITE_LICENSE_KEY এনভায়রনমেন্ট ভেরিয়েবল সেট করেন। এটি একটি ফ্রি MaxMind কি, যা Shlink ব্যবহার করে GeoLite2 ডেটাবেস ডাউনলোড করে। এটি ছাড়া ভিজিট রেকর্ড করা হলেও সেগুলোর ভৌগোলিক অবস্থান নির্ণয় করা যায় না।
ওয়েব ক্লায়েন্ট এবং QR কোড
ওয়েব ক্লায়েন্ট এখন 127.0.0.1:8081-এ রয়েছে এবং এর জন্য নিজস্ব প্রক্সি এন্ট্রি প্রয়োজন, অথবা আপনি যদি এটি পাবলিশ করতে না চান তবে একটি SSH টানেল ব্যবহার করতে পারেন। প্রথমবার লোড করার সময় এটি একটি সার্ভার URL এবং একটি API কী চায়। https://s.example.com এবং আপনার তৈরি করা কী-টি লিখুন। ক্লায়েন্ট এই দুটি তথ্য ব্রাউজারের স্টোরেজে রাখে এবং সরাসরি আপনার API-কে কল করে, তাই কোনো ডেটা অন্য কারো মাধ্যমে প্রবাহিত হয় না।
QR কোডের জন্য কোনো কনফিগারেশনের প্রয়োজন নেই। যেকোনো শর্ট URL-এর শেষে /qr-code যুক্ত করুন এবং API ইমেজটি রিটার্ন করবে।
https://s.example.com/docs/qr-code?size=500&format=svg&margin=20size হলো পিক্সেলের মাপের প্রস্থ এবং এটি 50 থেকে 1000 পর্যন্ত গ্রহণ করে, যার ডিফল্ট মান 300। format হলো png অথবা svg। margin হলো কোডের চারপাশের খালি জায়গা পিক্সেল হিসেবে, এবং চূড়ান্ত ইমেজের পরিমাপ হলো সাইজ এবং মার্জিনের দ্বিগুণ। এমন কোডের জন্য errorCorrection=Q যুক্ত করুন যা ছোট করে প্রিন্ট করা হলে বা আংশিক ঢাকা থাকলেও স্ক্যান করা যায়।
সচল রাখুন
একটি শর্টেনার নীরবে অকার্যকর হয়ে যেতে পারে। লিঙ্কগুলো রিডাইরেক্ট করা বন্ধ করে দেয় এবং কেউ আপনাকে জানায় না, কারণ ব্যবহারকারী মনে করেন লিঙ্কটি মৃত। হোম পেজের পরিবর্তে একটি আসল শর্ট URL-এ আপটাইম চেক সেট করুন এবং রিডাইরেক্ট ছাড়া অন্য যেকোনো কিছুতে অ্যালার্ট দিন। একটি সেলফ-হোস্টেড Uptime Kuma ইনস্ট্যান্স এটি ভালোভাবে করতে পারে এবং এটি একটি নির্দিষ্ট স্ট্যাটাস কোডের জন্য নজর রাখতে পারে।
কন্টেইনার নয়, বরং ডাটাবেসের ব্যাকআপ নিন। একটি কমান্ড এটি ডাম্প করে।
sudo docker compose exec -T database pg_dump -U shlink shlink | gzip > shlink-$(date +%F).sql.gzসেই ফাইল এবং আপনার compose ফাইলটি নতুন সার্ভারে পুরো সার্ভিসটি পুনরায় তৈরি করতে পারে। আপগ্রেড করার নিয়ম হলো sudo docker compose pull এবং তারপর sudo docker compose up -d, এবং Shlink শুরুর সময় যেকোনো নতুন মাইগ্রেশন রান করে। পুল করার আগে ডাম্পটি নিয়ে নিন, কারণ একটি মাইগ্রেশন রোল ব্যাক করা যায় না।
FAQ
রিভার্স প্রক্সি যোগ করার পর আমার শর্ট লিঙ্কগুলো কেন 404 এরর দেখাচ্ছে?
Shlink Host হেডারে থাকা ডোমেইনের সাথে শর্ট কোড মিলিয়ে দেখে। কোনো প্রক্সি যদি তার নিজস্ব নাম বা অভ্যন্তরীণ ঠিকানা পাঠায়, তবে Shlink সেই ডোমেইনের অধীনে কোডটি খুঁজতে গিয়ে কোনো লিঙ্ক পায় না, তাই এটি 404 রেসপন্স দেয়। nginx লোকেশন ব্লকে proxy_set_header Host $host; সেট করুন এবং প্রক্সি রিলোড করুন। কন্টেইনার রিস্টার্ট না করেই লিঙ্কগুলো সাথে সাথে কাজ করা শুরু করবে।
আমার কি Postgres প্রয়োজন, নাকি SQLite যথেষ্ট?
Shlink পরীক্ষা করে দেখার জন্য SQLite যথেষ্ট এবং এর জন্য আলাদা কোনো কন্টেইনারের প্রয়োজন হয় না। গুরুত্বপূর্ণ লিঙ্কগুলো পাবলিশ করার আগে Postgres-এ চলে যান, কারণ প্রতিটি ক্লিকের সাথে ভিজিট রো (row) বাড়তে থাকে এবং SQLite রাইট অপারেশনগুলোকে সিরিয়ালাইজ করে। পরবর্তীতে সুইচ করতে চাইলে আপনার লিঙ্কগুলো এক্সপোর্ট এবং পুনরায় ইমপোর্ট করতে হবে, তাই শুরুতেই Postgres বেছে নিলে এই মাইগ্রেশনের ঝামেলা এড়ানো যায়।
আমি কি ভুলে যাওয়া API কি (key) পুনরুদ্ধার করতে পারি?
না। Shlink কি-এর একটি হ্যাশ সংরক্ষণ করে, তাই api-key:list শুধুমাত্র নাম এবং স্ট্যাটাস দেখায়, কিন্তু কখনোই মূল ভ্যালু দেখায় না। shlink api-key:generate ব্যবহার করে একটি নতুন কি তৈরি করুন, সেটি ওয়েব ক্লায়েন্টে পেস্ট করুন এবং তারপর shlink api-key:disable ব্যবহার করে পুরনোটি ডিজেবল করে দিন যাতে সেটি আর কাজ না করে।
আমার ভিজিট স্ট্যাটাসে কান্ট্রি কলামগুলো খালি কেন?
জিওলোকেশন বা ভৌগোলিক অবস্থানের জন্য GeoLite2 ডাটাবেস প্রয়োজন, যা Shlink শুধুমাত্র তখনই ডাউনলোড করে যখন আপনি একটি GEOLITE_LICENSE_KEY প্রদান করেন। এই কি-টি MaxMind থেকে বিনামূল্যে পাওয়া যায়। এটিকে এনভায়রনমেন্ট সেকশনে যোগ করুন, কন্টেইনারটি পুনরায় তৈরি করুন, তাহলে নতুন ভিজিটগুলোর অবস্থান শনাক্ত করা যাবে। এর আগে রেকর্ড করা ভিজিটগুলো খালিই থাকবে যতক্ষণ না আপনি shlink visit:locate রান করছেন।
আমি কীভাবে Shlink অন্য সার্ভারে স্থানান্তর করব?
ডোমেইন ঠিক রেখে ডাটা স্থানান্তর করুন। pg_dump ব্যবহার করে ডাটাবেস ডাম্প করুন, ডাম্প ফাইল এবং compose ফাইলটি নতুন সার্ভারে কপি করুন, স্ট্যাকটি চালু করুন এবং আসল ট্রাফিক আসার আগেই খালি ডাটাবেসে ডাম্পটি রিস্টোর করুন। সবশেষে DNS রেকর্ড পরিবর্তন করুন। শর্ট কোড এবং সেগুলোর ভিজিট হিস্ট্রি অক্ষত থাকবে, কারণ সবকিছু ডাটাবেসের মধ্যেই থাকে।