Oracle Free TierでShlinkを構築|独自ドメイン短縮URLサービスを無料で立ち上げた話

技術

はじめに

短縮URLサービスといえば bit.lyis.gd が有名です。SNSやブログなどで「長すぎるURLを短くする」だけでなく、クリック数を解析したり、ブランド感を出したりするのに便利です。

ただし、商用サービスを使う場合には以下のような制約があります。

  • 無料プランだとドメインが共用で、独自ドメインが使えない
  • 詳細なアクセス解析や高度な機能は有料課金が必要
  • サービスの仕様変更や終了リスクに依存する

そこで今回は、自前の環境にOSSの「Shlink」を構築し、独自ドメインで短縮URLサービスを運用する方法をまとめました。

Shlinkは以下の特徴を持つOSSです。

  • 独自ドメインに完全対応(例: https://kty.at/xxxxxx
  • APIを通じて短縮URLを生成でき、外部アプリやスクリプトとも連携しやすい
  • 管理用Webクライアントを使えばGUIで操作可能
  • クリック数やリファラーなどのアクセス解析も可能

さらに、今回は Oracle Cloud Free Tier を利用します。
無料枠で常時稼働できるサーバーを用意し、そこにShlinkを入れることで、**完全にゼロコストで「自分専用の短縮URLサービス」**を実現できます。

この記事を読み終える頃には、以下ができるようになります。

  • Oracle Free Tier上にUbuntuインスタンスを立てて、Shlinkを動かす
  • 独自ドメイン(例: kty.at)を割り当てて短縮URLを発行する
  • 管理UI(例: https://ui.kty.at)からブラウザ経由でURL管理
  • SSL証明書をLet’s Encryptで導入し、自動更新まで仕込む

環境

  • クラウド: Oracle Cloud Free Tier
    • VM.Standard.E2.1.Micro (AMD)
    • Ubuntu 22.04 LTS
  • アプリケーション: Shlink v4.5.0 (PHP 8.3 dist ZIP)
  • Webサーバー: Nginx
  • DB: MariaDB 10.11
  • PHP: 8.3
  • SSL証明書: Let’s Encrypt(Certbot 2.x snap版)

ドメイン・URL

  • 短縮ドメイン: kty.at (ご自身のドメインに読み替えてください)
  • 短縮URLの公開サーバー: https://kty.at
  • 管理UI (Web Client): https://ui.kty.at

Oracle Free Tierにインスタンスを作る方法(ざっくり)

  1. Oracle Cloudにサインアップ(クレカ必須だけど無料枠で使える)
  2. Compute → インスタンスの作成を選択
    • シェイプ: VM.Standard.E2.1.Micro(AMD、常時無料枠対象)
    • イメージ: Ubuntu 22.04 LTS を選択
  3. ネットワーク設定
    • VCN(Virtual Cloud Network)を作成
    • セキュリティリストで 80番/443番を許可
    • SSH鍵をアップロードして作成

インスタンスが立ち上がったら、登録した鍵で接続します:

ssh -i ~/.ssh/id_rsa ubuntu@<Public_IP>

iptables で 80/443 を空ける

# 80番 (HTTP) を許可
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# 443番 (HTTPS) を許可
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# 状態を保存
sudo apt install -y iptables-persistent
sudo netfilter-persistent save

確認:

sudo iptables -L -n

インスタンス初期設定

sudo apt update && sudo apt upgrade -y
sudo apt install -y unzip curl software-properties-common

PHP 8.3 のインストール

sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install -y php8.3 php8.3-cli php8.3-fpm php8.3-mysql \
  php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip php8.3-bcmath

確認:

php -v

MariaDBのセットアップ

sudo apt install -y mariadb-server mariadb-client
sudo systemctl enable --now mariadb
sudo mysql_secure_installation

DB作成:

CREATE DATABASE shlink DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'shlink_user'@'localhost' IDENTIFIED BY 'secure_password';
GRANT ALL PRIVILEGES ON shlink.* TO 'shlink_user'@'localhost';
FLUSH PRIVILEGES;

Shlinkのダウンロードと配置

cd /var/www
wget https://github.com/shlinkio/shlink/releases/download/v4.5.0/shlink4.5.0_php8.3_dist.zip
unzip shlink4.5.0_php8.3_dist.zip -d shlink
cd shlink

権限調整:

sudo mkdir -p data/locks
sudo chown -R www-data:www-data /var/www/shlink

インストーラーで初期設定

php vendor/bin/shlink-installer install

主な質問と回答例

  • DATABASE: MariaDB, Host 127.0.0.1, Port 3306, DB shlink, User shlink_user, Password secure_password
  • URL SHORTENER: Default domain kty.at, HTTPS → Yes
  • その他は既定のままでOK

Nginx設定

Shlink本体 (API/リダイレクト)

/etc/nginx/sites-available/shlink

# --- 80番: HTTPSへリダイレクト(ACMEは通す) ---
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name kty.at _;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/shlink/public;
    }

    return 301 https://$host$request_uri;
}

# --- 443番: 本体(短縮URL + /rest API) ---
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name kty.at;

    ssl_certificate /etc/letsencrypt/live/kty.at/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/kty.at/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    root  /var/www/shlink/public;
    index index.php;
    charset utf-8;

    location ^~ /rest/ {
        if ($request_method = OPTIONS) {
            add_header Access-Control-Allow-Origin "https://ui.kty.at";
            add_header Access-Control-Allow-Credentials "true";
            add_header Access-Control-Allow-Headers "X-Api-Key, x-api-key, Authorization, Content-Type";
            add_header Access-Control-Allow-Methods "GET,POST,PUT,PATCH,DELETE,OPTIONS";
            return 204;
        }
        add_header Access-Control-Allow-Origin "https://ui.kty.at" always;
        add_header Access-Control-Allow-Credentials "true" always;
        try_files $uri /index.php$is_args$args;
    }

    location / {
        try_files $uri /index.php$is_args$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~* ^/(config|data|\.env|composer\.(json|lock)) { deny all; }

    access_log /var/log/nginx/shlink.access.log;
    error_log  /var/log/nginx/shlink.error.log warn;
}

Shlink Web Client (管理UI)

/etc/nginx/sites-available/shlink-web

server {
  listen 80;
  listen [::]:80;
  server_name ui.kty.at;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name ui.kty.at;

  ssl_certificate /etc/letsencrypt/live/ui.kty.at/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/ui.kty.at/privkey.pem; # managed by Certbot
  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

  root /var/www/shlink-web;
  index index.html;
  charset utf-8;

  auth_basic "Restricted";
  auth_basic_user_file /etc/nginx/.htpasswd;

  location ~* \.(?:manifest|appcache|html?|xml|json)$ { expires -1; }
  location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
      expires 1M; add_header Cache-Control "public";
  }
  location ~* \.(?:css|js)$ {
      expires 1y; add_header Cache-Control "public, immutable";
  }

  location = /service-worker.js {
      add_header Cache-Control "no-cache";
      try_files $uri =404;
  }

  location = /servers.json {
      try_files /servers.json /conf.d/servers.json =404;
  }

  location ~* .+\.(css|js|html|png|jpe?g|gif|bmp|ico|json|csv|otf|eot|svg|svgz|ttf|woff|woff2|ijmap|pdf|tif|map)$ {
      try_files $uri $uri/ =404;
  }

  location / {
      try_files $uri $uri/ /index.html$is_args$args;
  }

  access_log /var/log/nginx/shlink-web.access.log;
  error_log  /var/log/nginx/shlink-web.error.log warn;
}

設定を有効化する方法

Nginxは sites-available/ に置いた設定を sites-enabled/ にシンボリックリンクする仕組みです。
作成した設定を有効化し、テストしてからリロードします。

# シンボリックリンクを作成
sudo ln -s /etc/nginx/sites-available/shlink /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/shlink-web /etc/nginx/sites-enabled/

# 設定テスト
sudo nginx -t

# 問題なければリロード
sudo systemctl reload nginx

ブラウザで https://kty.athttps://ui.kty.at にアクセスできればOKです。


Shlink Web Clientの導入

cd /var/www
wget https://github.com/shlinkio/shlink-web-client/releases/download/v4.5.1/shlink-web-client_4.5.1_dist.zip
unzip shlink-web-client_4.5.1_dist.zip -d shlink-web

servers.json の作成

[
  {
    "name": "My Shlink Server",
    "url": "https://kty.at",
    "apiKey": "xxxxxx-xxxxxx-xxxxxx-xxxxxx"
  }
]

これを /var/www/shlink-web/servers.json に置くと、UI (https://ui.kty.at) にアクセスしたときに自動でサーバー登録されます。

APIキーはインストーラーで発行されますが、後から CLI で再発行も可能です。

php bin/cli api-key:generate

SSL設定とCertbotの自動更新

snap版 certbot の導入

sudo apt remove certbot python3-certbot-nginx -y
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

証明書の発行

sudo certbot --nginx -d kty.at -d ui.kty.at

自動更新の仕組み

systemctl list-timers | grep certbot

snap.certbot.renew.timer が1日2回走っていればOK。

Nginx reload のフック追加

sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
sudo tee /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh

テスト(dry-run)

sudo certbot renew --dry-run

よくあるトラブル

  • FlockStoreの権限不足: /var/www/shlink/data/lockswww-data に変更
  • CORS問題: x-api-key が弾かれる → NginxのCORSヘッダに追加
  • ドメイン変更: 再インストール不要。config/params/generated_config.phpdefault_domain を編集し PHP-FPM をリロード

まとめ

今回は Oracle Cloud Free Tier を使って、OSSの Shlink をセットアップし、独自ドメイン kty.at で短縮URLサービスを立ち上げる手順を解説しました。

これで https://kty.at/Glnn4 というような短縮URLの発行ができるようになりました。

手順を振り返ると、以下のステップで構築できました。

  1. Oracle Cloudで 無料インスタンスを作成し、iptablesで 80/443 を開放
  2. PHP 8.3MariaDB をインストールして環境を整備
  3. Git clone ではなく 配布ZIPからShlink本体を導入
  4. インストーラーで初期設定(DB・ドメイン・HTTPS有効化・APIキー発行)
  5. Nginxの設定kty.at に短縮URLサービス、ui.kty.at に管理UIを割り当て
  6. Let’s Encrypt + Certbot で無料SSL証明書を導入、自動更新まで設定
  7. Shlink Web Client を追加し、servers.json にAPIキーを登録してGUIで操作可能に

この構成で得られるメリット

  • ゼロコスト運用: Oracle Free Tier の範囲内で、完全無料で自前の短縮URLサービスを維持できる
  • ブランディング性: 独自ドメイン(例: https://kty.at/xxxxxx)で短縮URLを発行でき、第三者サービスに依存しない
  • 安心のSSL環境: Let’s Encrypt + 自動更新フックで証明書管理の手間が不要
  • 柔軟な運用: Web UIで手軽に使えるだけでなく、API連携でアプリやスクリプトからも活用可能
  • カスタマイズ性: NginxやShlinkの設定を変更すれば、自分の用途に合わせて拡張できる

今後の拡張アイデア

  • アクセス解析の活用: ShlinkのAPIを活用して、ダッシュボードや外部BIツールと連携
  • 複数ドメイン対応: 設定を追加すれば複数の短縮ドメインを使い分けられる
  • 自動化ワークフロー: APIキーを使って、ブログ記事公開時に短縮URLを自動生成する仕組みを組み込む
  • リソース制約に注意: Free TierはCPUやメモリが限られるため、アクセスが集中するサービスには不向き。商用規模になったら有料インスタンスや他クラウドへの移行も検討

結果として、「bit.ly に依存せず、自分だけの独自ドメイン短縮URLサービスを無料で構築」できました。
これはちょっとした遊び用途にも、ポートフォリオとしても、実務の効率化にも役立つはずです。

コメント

タイトルとURLをコピーしました