SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-30

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

本指南說明如何於 VPS 部署 Shlink 網址縮短服務。包含 Postgres 資料庫配置、DNS 設定、API 金鑰管理及反向代理設定。透過 Docker Compose 實作,確保您的短網址系統具備統計分析與 QR Code 生成功能。

您正在建置的內容

自架網址縮短服務是一台小型伺服器,能將長連結轉換為您擁有的短連結,並統計每次點擊。Shlink 是首選方案:它是開源軟體,以 Docker image 形式發布,並透過一個容器搭配資料庫完成所有工作。本指南將其部署在 VPS 上,並使用真實的短網域,同時配置 HTTPS、API key、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"

docker exec -it <container_name> ./app-cli key generate --name <key_name>

該指令僅會顯示金鑰一次。請立即複製,因為系統儲存的是雜湊值,無法再次顯示。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

curl -H "Authorization: Bearer <your_key>" https://api.example.com/status

若回傳包含 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 可以重複使用,標籤(tags)則是您後續彙整統計數據時的分類依據。

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

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 環境變數,否則國家與城市欄位將保持空白;該變數為免費的 MaxMind 金鑰,供 Shlink 下載 GeoLite2 資料庫使用。若未設定,系統仍會記錄造訪次數,僅無法進行地理定位。

網頁客戶端與 QR Code

網頁客戶端現位於 127.0.0.1:8081,需要設定專屬的代理項目;若您不想公開存取,亦可改用 SSH tunnel。首次載入時,系統會要求輸入伺服器 URL 與 API key。請輸入 https://s.example.com 以及您所產生的金鑰。客戶端會將兩者儲存於瀏覽器儲存空間,並直接呼叫您的 API,因此資料不會經由第三方傳輸。將介面與 API 分離是一種值得注意的模式,這與 Halcyon 將 Jellyfin 媒體庫包裝成 1990 年代錄影帶出租店 的原理相同,且無需更動後端的媒體伺服器。

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 周圍的留白空間(單位為像素),最終產出的圖片尺寸為設定大小加上兩倍的邊距。若需在列印尺寸較小或部分遮蔽的情況下仍能掃描,請加入 errorCorrection=Q

維持服務運作

縮網址服務若失效通常不會有明顯錯誤。連結會停止重新導向,且不會有人通知你,因為點擊者會直接認定該連結已失效。請將監控服務指向實際的縮網址,而非首頁,並針對任何非重新導向的狀態進行告警。自架的 Uptime Kuma 實例 能妥善處理此需求,並可監控特定的狀態碼。

請備份資料庫,而非容器。執行單一指令即可匯出資料庫。

sudo docker compose exec -T database pg_dump -U shlink shlink | gzip > shlink-$(date +%F).sql.gz

該檔案加上你的 compose 檔案,即可在新的伺服器上重建整個服務。伺服器上的每個應用程式都需要各自的這對檔案。相片庫是較特殊的案例,因為 PhotoPrism 與 Immich 同時將原始檔案存放在磁碟中,並在資料庫中建立記錄,因此僅靠資料庫匯出檔無法還原資料。升級步驟為先執行 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 key,可以找回嗎?

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

為什麼訪問統計中的國家欄位是空的?

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

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

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