SSD Nodes Learn 8GB 記憶體 — 每年 $66
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-01

如何使用 Shlink 與 Docker 自架 URL 縮網址服務

透過 Docker Compose 在 VPS 上部署 Shlink。本指南涵蓋 Postgres 資料庫設定、DNS 網域綁定、API 金鑰管理及 HTTPS 安全配置,助您建立專屬的短網址系統並追蹤點擊數據。

您將建置的內容

自架 URL 縮網址服務是一個小型伺服器,能將長連結轉換為您擁有的短連結,並計算每次點擊。Shlink 是首選方案:它是開源軟體,以 Docker 映像檔形式發布,並在單一容器搭配資料庫中完成所有工作。本指南將其部署在 VPS 上,並使用真實的短網域、HTTPS、API 金鑰、QR Code 以及點擊統計功能。

兩個組件使其具備商業級縮網址服務的體驗。API 伺服器負責處理重新導向並儲存資料。網頁客戶端是一個獨立的靜態應用程式,透過瀏覽器與該 API 溝通。您可以同時執行兩者,或僅執行 API 並透過命令列進行操作。

此處的版本號為 2026 年 7 月時的最新版本:Shlink 5.1 與 shlink-web-client 4.8。

先將短網域指向伺服器

網域即是產品。s.example.com/abc123 是使用者看到的連結,因此請選擇一個簡短的網域,並在安裝任何軟體前完成設定。Shlink 會將網域儲存於每個短網址中,若事後變更網域,所有已發佈的連結將會失效。

為該短網域建立一筆 DNS A 記錄,指向您的 VPS 公用 IPv4 位址。若伺服器支援 IPv6,請一併新增 AAAA 記錄。在繼續後續步驟前,請先確認網域解析正常。

dig +short s.example.com A

輸出結果應為您的伺服器位址。若結果為空,代表記錄尚未生效;後續步驟將會因無法解析網域名稱而導致 TLS (transport layer security) 憑證簽發失敗,進而引發難以排查的錯誤。

Compose 檔案

Shlink 需要資料庫。SQLite 適用於測試,但若您打算長期使用,Postgres 是正確的選擇,因為訪問紀錄會持續累積,而 Postgres 能更有效地處理索引與並發寫入。請將此內容放入 /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,因此在下一節設定反向代理之前,網際網路無法存取該服務。Docker 會在主機防火牆之前寫入自身的轉發規則,這意味著單純的 8080:8080 設定即使在防火牆看似關閉的主機上,也會暴露應用程式。綁定至 loopback 位址可避免此問題。此模式適用於任何以相同方式執行的應用程式,詳情請參閱 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。若事後修改密碼,除非移除 volume 並重新啟動,否則不會生效。

在前端終止 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.com

compose 檔案中的 IS_HTTPS_ENABLED: "true" 設定決定了 Shlink 在回傳短網址時是否使用 https://。此設定本身不會啟用 TLS。若將其置於 HTTPS 代理伺服器後方並設為 false,API 回傳的每個連結都會是 http:// 連結,這會導致額外的來回傳輸,且在網頁客戶端中顯示異常。

建立 API 金鑰

若無金鑰,任何程式皆無法與 API 通訊。請在容器內透過 CLI 產生金鑰。

sudo docker compose exec shlink shlink api-key:generate --name "web client"

此指令僅會顯示該金鑰一次。請立即複製,因為系統儲存的是雜湊值,無法再次顯示。shlink api-key:list 可列出金鑰名稱及其啟用狀態,但不會顯示金鑰本身。若要撤銷金鑰,請使用 shlink api-key:disable 並指定名稱。

每次 REST 呼叫皆須在 X-Api-Key 標頭中攜帶此金鑰。

curl -H "X-Api-Key: YOUR_KEY" https://s.example.com/rest/v3/short-urls

若收到包含 shortUrls 金鑰的 JSON 物件,表示金鑰運作正常。若收到 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 可讓您取得易讀的連結,而非隨機產生的代碼。Slug 在每個網域中皆為唯一,因此若嘗試使用已存在的 slug,系統會失敗,而不會在未經提示的情況下覆蓋先前的連結。--tag 可以重複使用,標籤則是您用來分組連結的方式,以便日後合併統計數據。

列出現有的連結,接著查看單一連結的流量。

sudo docker compose exec shlink shlink short-url:list
sudo docker compose exec shlink shlink short-url:visits docs

short-url:visits 會針對每次點擊列印一行資料,包含日期、參照連結網址 (referrer) 與使用者代理 (user agent)。除非您設定了 GEOLITE_LICENSE_KEY 環境變數,否則國家與城市欄位將保持空白;該變數為 Shlink 用於下載 GeoLite2 資料庫所需的免費 MaxMind 金鑰。若未設定,系統仍會記錄造訪次數,只是不會進行地理定位。

Web 客戶端與 QR Code

Web 客戶端現位於 127.0.0.1:8081,需要專屬的代理項目;若您不想公開該服務,亦可使用 SSH tunnel。首次載入時,系統會要求輸入伺服器 URL 與 API key。請輸入 https://s.example.com 以及您產生的金鑰。客戶端會將兩者儲存於瀏覽器儲存空間,並直接呼叫您的 API,因此資料不會經由第三方傳輸。

QR Code 無需任何設定。在任何短網址後方附加 /qr-code,API 即會回傳該影像。

https://s.example.com/docs/qr-code?size=500&format=svg&margin=20

size 為寬度(單位為像素),接受 50 至 1000 之間的值,預設值為 300。format 可設為 pngsvgmargin 為 QR Code 周圍的留白空間(單位為像素),最終影像尺寸為設定寬度加上兩倍的邊距。若 QR Code 在列印較小或部分遮蔽時仍需保持可掃描性,請加入 errorCorrection=Q

維持服務運作

縮網址服務若發生故障,通常不會有明顯錯誤訊息。連結會停止重新導向,且不會通知管理員,因為使用者通常會誤以為連結已失效。請將監控服務指向實際的縮網址,而非首頁,並針對任何非重新導向的狀態進行告警。自架的 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 會在啟動時自動執行任何新的遷移程序。請務必在執行 pull 之前進行備份,因為遷移程序無法復原。

FAQ

為什麼在加入反向代理後,我的短連結會回傳 404?

Shlink 會根據 Host 標頭中的網域來比對短代碼。若代理伺服器發送的是其自身的名稱或內部位址,Shlink 就會嘗試在該網域下尋找代碼,但因該網域並無任何連結,故回傳 404。請在 nginx 的 location 區塊中設定 proxy_set_header Host $host; 並重新載入代理伺服器。連結將立即恢復運作,無需重新啟動容器。

我需要使用 Postgres,還是 SQLite 就足夠了?

SQLite 適合用於測試 Shlink,且無需額外的容器。若您要發布重要的連結,請改用 Postgres,因為訪問紀錄會隨點擊次數增加,而 SQLite 會將寫入操作序列化。若事後才轉換,則必須匯出並重新匯入連結,因此在初期選擇 Postgres 可避免後續的遷移作業。

我可以找回忘記複製的 API 金鑰嗎?

不行。Shlink 僅儲存金鑰的雜湊值,因此 api-key:list 僅顯示名稱與狀態,不會顯示金鑰內容。請使用 shlink api-key:generate 產生新的金鑰,將其貼至網頁客戶端,並使用 shlink api-key:disable 停用舊金鑰以使其失效。

為什麼我的訪問統計中,國家/地區欄位是空的?

地理位置功能需要 GeoLite2 資料庫,Shlink 僅在您提供 GEOLITE_LICENSE_KEY 時才會下載該資料庫。此金鑰可從 MaxMind 免費取得。請將其加入環境變數區塊並重建容器,新的訪問紀錄即可顯示地理位置。在此之前記錄的訪問紀錄將保持空白,除非您執行 shlink visit:locate

請保留網域並遷移資料。使用 pg_dump 匯出資料庫,將匯出檔案與 compose 檔案複製到新伺服器,啟動堆疊,並在實際流量進入前將匯出檔案還原至空的資料庫中。最後再變更 DNS 紀錄。由於所有資料皆儲存在資料庫中,短代碼及其訪問歷史將會完整保留。

#shlink#url-shortener#self-hosting#docker#postgres