9
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【後半】ラズパイ3を自宅Webサーバーとして利用する手順書

9
Posted at

はじめに

この記事は、前半記事の続きです。

前半では、Claude CodeからRaspberry PiへのSSH接続設定を行いました。
今回は、独自ドメインでHTTPS対応の自宅サーバーを構築する手順を、初心者の方でもClaude Codeなしで再現できるよう、詳しく解説します。

この記事で実現できること

  • www.example.com → React + Viteアプリ
  • seisan.example.com → LINE Bot(精算くん)
  • bus.example.com → LINE Bot(バス情報)

すべてHTTPSで安全にアクセス可能、ngrokなしで独自ドメインで公開できます。

この記事ではドメイン名・IPアドレス・ユーザー名などをすべて伏せ字にしています。ご自身の環境に合わせて読み替えてください。

  • ドメイン: example.com
  • ラズパイの内部IP: 192.168.x.x
  • グローバルIP: xxx.xxx.xxx.xxx
  • DDNSドメイン: yourname.tplinkdns.com
  • ラズパイのユーザー名: myuser

所要時間

  • 設定作業: 約2〜3時間
  • DNS浸透待ち: 30分〜1時間

構成環境

項目 内容
ラズパイ Raspberry Pi 3
OS Raspberry Pi OS (Debian bookworm)
Webサーバー Nginx 1.22.1
ランタイム Node.js 18.x、Python 3.11
ルーター TP-Link Deco(DDNS機能あり)
ドメイン取得 お名前.com
SSL証明書 acme.sh + ZeroSSL(ワイルドカード証明書)

前提条件

以下が完了していることを確認してください:

  • 前半記事のSSH接続設定が完了
  • 独自ドメインを取得済み(例: example.com
  • ルーターにDDNS機能がある(またはグローバル固定IP)
  • 各サービスがラズパイのローカルポートで動作中
    • 例: Vite (5173)、バックエンド (3001)、LINE Bot (3000, 5000)

📋 全体の流れ

1. ネットワーク設定
   ├─ ラズパイの静的IP予約
   ├─ DDNS設定(動的IP対策)
   └─ ポートフォワーディング(80, 443を開放)

2. DNS設定(お名前.com)
   ├─ サブドメインのCNAMEレコード追加
   └─ DNS浸透確認

3. Nginx設定(HTTP)
   ├─ Nginxインストール
   ├─ サイトごとの設定ファイル作成
   └─ HTTP動作確認

4. SSL証明書取得(acme.sh)
   ├─ acme.shインストール
   ├─ DNS Challengeでワイルドカード証明書取得
   └─ 証明書をNginxに配置

5. Nginx HTTPS化
   ├─ 各サイトにHTTPS設定追加
   └─ HTTPS動作確認

6. LINE Bot Webhook変更
   ├─ LINE DevelopersでWebhook URL更新
   └─ ngrok停止

7. セキュリティ設定
   ├─ ufwファイアウォール有効化
   └─ ルーター側セキュリティ機能ON

8. 自動更新設定
   └─ SSL証明書の自動更新確認

ステップ1: ネットワーク設定

1-1. ラズパイの静的IP予約

なぜ必要?
ラズパイのIPアドレスが変わると、ルーターのポートフォワーディング設定が無効になります。

手順(TP-Link Decoアプリの場合):

  1. スマホでDecoアプリを開く
  2. 画面下部の「デバイス」タブをタップ
  3. デバイス一覧から「Raspberry Pi」を探してタップ
  4. 「アドレス予約」をタップ
  5. スイッチをONにする
  6. IPアドレスを 192.168.x.x に設定(好きな値を選んでOK)
  7. 保存ボタンをタップ

確認方法:

# Macから実行
ping 192.168.x.x
# 応答があればOK

1-2. DDNS設定

なぜ必要?
家庭用インターネットは通常、グローバルIPが定期的に変わります。DDNSを使うと、IPが変わっても常に同じドメイン名でアクセスできます。

手順(TP-Link Decoアプリの場合):

  1. Decoアプリを開く
  2. 画面下部の「その他」タブをタップ
  3. 「詳細設定」→「DDNS」をタップ
  4. 「TP-Link DDNS」をONにする
  5. サブドメイン名を入力(例: yourname
    • 完成形: yourname.tplinkdns.com
  6. 保存ボタンをタップ

確認方法:

# 現在のグローバルIPを確認
curl ifconfig.me

# DDNSドメインが正しいIPを返すか確認
ping yourname.tplinkdns.com
# 上記のグローバルIPと一致すればOK

注意:

  • サブドメイン名は他の人と重複しない名前にしてください
  • 設定後、反映まで数分かかる場合があります

1-3. ポートフォワーディング設定

なぜ必要?
デフォルトでは、外部からのアクセスはルーターでブロックされます。ポートフォワーディングで「外部ポート80/443への接続をラズパイに転送する」設定をします。

手順(TP-Link Decoアプリの場合):

  1. Decoアプリを開く
  2. 「その他」→「詳細設定」→「ポートフォワーディング」
  3. 右上の「+」ボタンをタップ
  4. 1つ目のルール(HTTP):
    • サービス名: HTTP
    • 外部ポート: 80
    • 内部IP: 192.168.x.x(ラズパイのIP)
    • 内部ポート: 80
    • プロトコル: TCP
    • 保存
  5. 2つ目のルール(HTTPS):
    • サービス名: HTTPS
    • 外部ポート: 443
    • 内部IP: 192.168.x.x
    • 内部ポート: 443
    • プロトコル: TCP
    • 保存

確認方法:

# 外部からポート80にアクセスできるか確認
curl -I http://$(curl -s ifconfig.me)
# エラーが返ってくればOK(まだNginxを設定していないため)

よくあるミス:

  • ❌ 内部IPを間違える(必ずラズパイのIPを指定)
  • ❌ プロトコルをUDPにしてしまう(必ずTCP)
  • ❌ ルーターの設定画面にアクセスできない場合
    • → ルーターの管理画面のIPアドレスを確認(通常192.168.1.1か192.168.0.1)

ステップ2: DNS設定(お名前.com)

2-1. DNSレコード設定

なぜこの設定?

  • CNAME: サブドメイン(www, seisan, bus)をDDNSドメインに紐付け
  • A: ルートドメイン(@)は直接グローバルIPを指定

手順:

  1. お名前.com Navi にログイン
  2. 「ドメイン」タブをクリック
  3. 対象ドメイン(例: example.com)の「DNS」をクリック
  4. 「DNS設定/転送設定」→「DNSレコード設定を利用する」
  5. 以下のレコードを1つずつ追加:
ホスト名 TYPE TTL VALUE 優先
www CNAME 3600 yourname.tplinkdns.com -
seisan CNAME 3600 yourname.tplinkdns.com -
bus CNAME 3600 yourname.tplinkdns.com -
(空欄) A 3600 (グローバルIP) -

グローバルIPの確認方法:

curl ifconfig.me
# 例: xxx.xxx.xxx.xxx

入力例(www の場合):

ホスト名: www
TYPE: CNAME
TTL: 3600
VALUE: yourname.tplinkdns.com

重要な注意点:

  • ✅ VALUEの末尾に . (ドット) は不要(お名前.comの場合)
  • ✅ ホスト名が「@」の場合は空欄にする
  • ✅ 既存のレコードがある場合は削除してから追加
  1. すべて入力したら「確認画面へ進む」→「設定する」

2-2. DNS浸透確認

なぜ待つ必要がある?
DNS設定は世界中のDNSサーバーに伝わるまで時間がかかります(通常30分〜1時間)。

確認コマンド:

# Google DNS (8.8.8.8) に問い合わせ
dig www.example.com @8.8.8.8

成功例(出力の一部):

;; ANSWER SECTION:
www.example.com. 3600 IN CNAME yourname.tplinkdns.com.
yourname.tplinkdns.com. 600 IN A xxx.xxx.xxx.xxx

浸透していない場合:

;; ANSWER SECTION:
(何も表示されない、またはパーキングページのIP)

待ち時間中にできること:

  • 次のステップ3(Nginx設定)を先に進めておく
  • コーヒーを飲む ☕️

ステップ3: Nginx設定(HTTP)

3-1. Nginxインストール

Nginxとは?
Webサーバーソフトウェア。複数のアプリケーションを1つのサーバーで動かすための「振り分け役」です。

# MacのターミナルからSSH接続
ssh raspi

# ラズパイ上で実行
sudo apt update
sudo apt install -y nginx

インストール確認:

nginx -v
# nginx version: nginx/1.22.1 のように表示されればOK

Nginxの起動と自動起動設定:

sudo systemctl start nginx
sudo systemctl enable nginx

3-2. サイト別の設定ファイル作成

Nginxの設定ファイルの仕組み:

  • /etc/nginx/sites-available/ → 設定ファイルを保存する場所
  • /etc/nginx/sites-enabled/ → 有効な設定へのシンボリックリンク

📄 www.example.com 用の設定

sudo nano /etc/nginx/sites-available/zamurai

nanoエディタの使い方:

  • 貼り付け: Ctrl + Shift + V(Macは Cmd + V
  • 保存: Ctrl + O → Enter
  • 終了: Ctrl + X

貼り付ける内容:

server {
    listen 80;
    server_name www.example.com;

    # Let's Encrypt の検証用(後で使う)
    location ^~ /.well-known/acme-challenge/ {
        root /var/www/html;
        default_type "text/plain";
    }

    # /api/ で始まるリクエストはバックエンド(3001番ポート)へ
    location /api/ {
        proxy_pass http://127.0.0.1:3001/api/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # /ws で始まるリクエストはWebSocketへ
    location /ws {
        proxy_pass http://127.0.0.1:3001/ws;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }

    # その他のリクエストはVite(5173番ポート)へ
    location / {
        proxy_pass http://127.0.0.1:5173;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

設定の意味:

  • listen 80; → HTTPポート(80番)で待ち受け
  • server_name → このドメインでアクセスされたら、この設定を使う
  • proxy_pass → 実際のアプリケーションに転送
  • proxy_set_header → 元のリクエスト情報をアプリに伝える

📄 seisan.example.com 用の設定

sudo nano /etc/nginx/sites-available/seisan
server {
    listen 80;
    server_name seisan.example.com;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/html;
        default_type "text/plain";
    }

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

ポイント:

  • 精算くんは3000番ポートで動いているので proxy_pass http://127.0.0.1:3000;

📄 bus.example.com 用の設定

sudo nano /etc/nginx/sites-available/bus
server {
    listen 80;
    server_name bus.example.com;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/html;
        default_type "text/plain";
    }

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

ポイント:

  • バス情報は5000番ポートで動いているので proxy_pass http://127.0.0.1:5000;

📄 ルートドメインのリダイレクト設定

sudo nano /etc/nginx/sites-available/redirect
server {
    listen 80;
    server_name example.com;
    # example.com でアクセスされたら www にリダイレクト
    return 301 https://www.example.com$request_uri;
}

3-3. 設定の有効化

シンボリックリンクとは?
「ショートカット」のようなもの。sites-available/ の設定ファイルを sites-enabled/ からリンクして有効化します。

# 1つずつシンボリックリンクを作成
sudo ln -s /etc/nginx/sites-available/zamurai /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/seisan /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/bus /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/redirect /etc/nginx/sites-enabled/

既にリンクがある場合:

# エラーが出たら、既存のリンクを削除してから再実行
sudo rm /etc/nginx/sites-enabled/zamurai
sudo ln -s /etc/nginx/sites-available/zamurai /etc/nginx/sites-enabled/

3-4. Nginx設定テストと再起動

必ず実行してください!
設定ミスがあるとNginxが起動しなくなります。

# 設定ファイルの文法チェック
sudo nginx -t

成功例:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

エラーが出た場合:

nginx: [emerg] unexpected "}" in /etc/nginx/sites-available/zamurai:10

→ 10行目付近に余分な } があります。設定ファイルを見直してください。

問題なければNginx再起動:

sudo systemctl reload nginx

3-5. HTTP動作確認

ローカル(ラズパイ上)からの確認:

# ラズパイ上で実行
curl http://localhost
# HTMLが返ってくればOK

外部(Mac)からの確認:

# Macのターミナルで実行
curl http://www.example.com

よくあるエラーと対処法:

エラー 原因 対処法
Connection refused Nginxが起動していない sudo systemctl start nginx
502 Bad Gateway バックエンドアプリが起動していない アプリを起動
404 Not Found server_nameが一致していない 設定ファイルのserver_nameを確認

ステップ4: SSL証明書取得(acme.sh + DNS Challenge)

4-1. acme.shのインストール

acme.shとは?
SSL証明書を無料で取得・更新するツール。Let's Encrypt / ZeroSSL に対応しています。

# ラズパイにSSH接続した状態で実行
curl https://get.acme.sh | sh -s email=your-email@example.com

⚠️ your-email@example.com を自分のメールアドレスに変更してください

インストール後の設定反映:

source ~/.bashrc

インストール確認:

~/.acme.sh/acme.sh --version
# https://github.com/acmesh-official/acme.sh
# v3.1.3 のように表示されればOK

4-2. DNS Challengeでワイルドカード証明書取得

なぜDNS Challenge?

  • ❌ HTTP Challenge: プロキシ環境で失敗しやすい、サブドメインごとに証明書が必要
  • ✅ DNS Challenge: どんな環境でも動作、ワイルドカード証明書(*.example.com)が取得できる

ワイルドカード証明書のメリット:

  • 1つの証明書で www., seisan., bus. すべてカバー
  • 新しいサブドメインを追加しても証明書の再取得が不要

ステップ4-2-1: 証明書発行開始

~/.acme.sh/acme.sh --issue --dns -d example.com -d "*.example.com"

実行すると、以下のような出力が表示されます:

Add the following TXT record:
Domain: '_acme-challenge.example.com'
TXT value: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

Add the following TXT record:
Domain: '_acme-challenge.example.com'
TXT value: 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'

Please add the TXT records to the domains, and re-run with --renew.

⚠️ 重要: この値は毎回変わります!必ず表示された値をメモしてください


ステップ4-2-2: お名前.comでTXTレコード追加

手順:

  1. お名前.com Navi にログイン
  2. 「ドメイン」→ 対象ドメインの「DNS」
  3. 「DNSレコード設定を利用する」
  4. 1つ目のTXTレコードを追加:
    • ホスト名: _acme-challenge
    • TYPE: TXT
    • TTL: 3600
    • VALUE: (acme.shで表示された1つ目の値)
  5. 2つ目のTXTレコードも同様に追加:
    • ホスト名: _acme-challenge
    • TYPE: TXT
    • TTL: 3600
    • VALUE: (acme.shで表示された2つ目の値)
  6. 「確認画面へ進む」→「設定する」

よくあるミス:

  • ❌ VALUEの前後にスペースを入れてしまう
  • ❌ シングルクォート ' を入力してしまう(不要)
  • ❌ 1つしか追加しない(2つとも必要)

ステップ4-2-3: DNS浸透確認

なぜ待つ?
TXTレコードが世界中のDNSサーバーに伝わるまで待ちます(通常30秒〜5分)。

# Google DNS (8.8.8.8) に問い合わせ
dig _acme-challenge.example.com TXT @8.8.8.8

成功例(2つのTXTレコードが表示される):

;; ANSWER SECTION:
_acme-challenge.example.com. 3600 IN TXT "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
_acme-challenge.example.com. 3600 IN TXT "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"

まだ浸透していない場合:

  • 何も表示されない、または1つしか表示されない
  • → 数分待ってから再度実行

浸透を早める方法:

# 複数のDNSサーバーで確認
dig _acme-challenge.example.com TXT @8.8.8.8    # Google
dig _acme-challenge.example.com TXT @1.1.1.1    # Cloudflare
dig _acme-challenge.example.com TXT @208.67.222.222  # OpenDNS

ステップ4-2-4: 証明書発行完了

TXTレコードが浸透したら実行:

~/.acme.sh/acme.sh --renew -d example.com -d "*.example.com"

成功すると、以下のような出力が表示されます:

[Mon Mar 22 15:45:30 JST 2026] Cert success.
-----BEGIN CERTIFICATE-----
MIIFZTCCBEygAwIBAgISBN...(省略)
-----END CERTIFICATE-----
[Mon Mar 22 15:45:30 JST 2026] Your cert is in: /home/myuser/.acme.sh/example.com_ecc/example.com.cer
[Mon Mar 22 15:45:30 JST 2026] Your cert key is in: /home/myuser/.acme.sh/example.com_ecc/example.com.key
[Mon Mar 22 15:45:30 JST 2026] The intermediate CA cert is in: /home/myuser/.acme.sh/example.com_ecc/ca.cer
[Mon Mar 22 15:45:30 JST 2026] And the full chain certs is there: /home/myuser/.acme.sh/example.com_ecc/fullchain.cer

証明書が保存されたパス:

/home/myuser/.acme.sh/example.com_ecc/
├── example.com.cer  ← ドメイン証明書
├── example.com.key  ← 秘密鍵(絶対に公開しない)
├── ca.cer           ← 中間証明書
└── fullchain.cer    ← 証明書チェーン(Nginxで使用)

エラーが出た場合:

エラー 原因 対処法
Verify error TXTレコードが見つからない DNS浸透を再確認
Invalid domain ドメイン名のタイポ -d の値を確認
Rate limit exceeded 同じドメインで何度も失敗した 1時間待ってから再実行

4-3. 証明書をNginxにコピー

なぜコピーが必要?
証明書はユーザーのホームディレクトリにありますが、Nginxは通常rootで動作します。Nginxが読み込める場所にコピーします。

# SSL証明書用ディレクトリを作成
sudo mkdir -p /etc/nginx/ssl

# 証明書を一時ディレクトリにコピー(ユーザー権限で実行)
cp ~/.acme.sh/example.com_ecc/*.cer /tmp/
cp ~/.acme.sh/example.com_ecc/*.key /tmp/

# 一時ディレクトリからNginxのディレクトリに移動(root権限で実行)
sudo mv /tmp/fullchain.cer /etc/nginx/ssl/
sudo mv /tmp/example.com.cer /etc/nginx/ssl/
sudo mv /tmp/example.com.key /etc/nginx/ssl/

# パーミッション設定(セキュリティ対策)
sudo chmod 600 /etc/nginx/ssl/example.com.key  # 秘密鍵は所有者のみ読み取り可
sudo chmod 644 /etc/nginx/ssl/*.cer             # 証明書は誰でも読み取り可

確認:

ls -la /etc/nginx/ssl/

出力例:

-rw-r--r-- 1 root root 3749 Mar 22 15:50 fullchain.cer
-rw-r--r-- 1 root root 2156 Mar 22 15:50 example.com.cer
-rw------- 1 root root  227 Mar 22 15:50 example.com.key

重要: 秘密鍵(.key)のパーミッションが 600 になっていることを確認してください


ステップ5: Nginx HTTPS設定

HTTPS設定の基本方針

各サイトで以下の構成にします:

  1. HTTPサーバー(ポート80): HTTPSにリダイレクト
  2. HTTPSサーバー(ポート443): 実際のアプリケーションに転送

5-1. www.example.com をHTTPS化

sudo nano /etc/nginx/sites-available/zamurai

既存の内容をすべて削除して、以下に置き換えます:

# HTTP → HTTPS リダイレクト
server {
    listen 80;
    server_name www.example.com;

    # Let's Encrypt の検証用(証明書更新時に使用)
    location ^~ /.well-known/acme-challenge/ {
        root /var/www/html;
        default_type "text/plain";
    }

    # その他のリクエストはすべてHTTPSにリダイレクト
    return 301 https://$server_name$request_uri;
}

# HTTPS サーバー
server {
    listen 443 ssl http2;
    server_name www.example.com;

    # SSL証明書のパス
    ssl_certificate /etc/nginx/ssl/fullchain.cer;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    # SSL設定(セキュリティ強化)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';

    # Let's Encrypt の検証用(HTTPSでも必要)
    location ^~ /.well-known/acme-challenge/ {
        root /var/www/html;
        default_type "text/plain";
    }

    # /api/ で始まるリクエストはバックエンドへ
    location /api/ {
        proxy_pass http://127.0.0.1:3001/api/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # WebSocket接続
    location /ws {
        proxy_pass http://127.0.0.1:3001/ws;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # その他のリクエストはViteへ
    location / {
        proxy_pass http://127.0.0.1:5173;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

保存して終了: Ctrl + O → Enter → Ctrl + X


5-2. seisan.example.com をHTTPS化

sudo nano /etc/nginx/sites-available/seisan

既存の内容をすべて削除して、以下に置き換えます:

# HTTP → HTTPS リダイレクト
server {
    listen 80;
    server_name seisan.example.com;
    return 301 https://$server_name$request_uri;
}

# HTTPS サーバー
server {
    listen 443 ssl http2;
    server_name seisan.example.com;

    # SSL証明書(ワイルドカード証明書を使用)
    ssl_certificate /etc/nginx/ssl/fullchain.cer;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    # SSL設定
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/html;
        default_type "text/plain";
    }

    # プロキシ設定(LINE Bot バックエンド)
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

5-3. bus.example.com をHTTPS化

sudo nano /etc/nginx/sites-available/bus

既存の内容をすべて削除して、以下に置き換えます:

# HTTP → HTTPS リダイレクト
server {
    listen 80;
    server_name bus.example.com;
    return 301 https://$server_name$request_uri;
}

# HTTPS サーバー
server {
    listen 443 ssl http2;
    server_name bus.example.com;

    # SSL証明書(ワイルドカード証明書を使用)
    ssl_certificate /etc/nginx/ssl/fullchain.cer;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    # SSL設定
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/html;
        default_type "text/plain";
    }

    # プロキシ設定(LINE Bot バックエンド)
    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

5-4. ルートドメインもHTTPS化

sudo nano /etc/nginx/sites-available/redirect

既存の内容をすべて削除して、以下に置き換えます:

# HTTP → HTTPS リダイレクト
server {
    listen 80;
    server_name example.com;
    return 301 https://www.example.com$request_uri;
}

# HTTPS でも www にリダイレクト
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/nginx/ssl/fullchain.cer;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    return 301 https://www.example.com$request_uri;
}

5-5. Nginx設定テストと再読み込み

必ず実行してください!

# 設定ファイルのテスト
sudo nginx -t

成功例:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

問題なければ再読み込み:

sudo systemctl reload nginx

よくあるエラー:

エラー 原因 対処法
cannot load certificate 証明書のパスが間違っている ls /etc/nginx/ssl/ で確認
PEM_read_bio_X509_AUX() failed 証明書ファイルが壊れている 証明書を再取得
nginx: [emerg] bind() to 0.0.0.0:443 failed 既に443番ポートが使用中 sudo lsof -i :443 で確認

5-6. HTTPS動作確認

ターミナルからの確認:

# Macのターミナルから実行
curl -I https://www.example.com
curl -I https://seisan.example.com
curl -I https://bus.example.com

成功例:

HTTP/2 200
server: nginx/1.22.1
date: Sun, 22 Mar 2026 15:54:27 GMT
content-type: text/html

ブラウザからの確認:

  1. ブラウザで https://www.example.com にアクセス
  2. URLバーに「🔒」マークが表示されればOK
  3. 鍵マークをクリック → 「証明書」→ 発行者が「ZeroSSL」になっていることを確認

HTTPからのリダイレクト確認:

curl -I http://www.example.com

成功例(301リダイレクト):

HTTP/1.1 301 Moved Permanently
Server: nginx/1.22.1
Location: https://www.example.com/

ステップ6: LINE Bot Webhook URL変更

6-1. LINE Webhook URLの仕組み

現在の状態:

  • LINE → ngrokのURL(例: https://xxxx.ngrok-free.app/webhook)→ ラズパイ

変更後:

  • LINE → 独自ドメイン(例: https://seisan.example.com/webhook)→ ラズパイ

6-2. エンドポイントの確認

重要: LINE Botごとにエンドポイントが異なります

アプリのソースコードでWebhookのエンドポイントを確認してください。

  • 精算くん → /webhook
  • バス情報 → /callback

のように、アプリによって異なります。


6-3. LINE Developersコンソールでの設定

精算くんの設定

  1. LINE Developers Console にログイン
  2. 「プロバイダー」→ 対象のプロバイダーを選択
  3. 「精算くん」のチャネルをクリック
  4. 左メニューの「Messaging API」をクリック
  5. 「Webhook URL」の項目を探す
  6. 「編集」ボタンをクリック
  7. 新しいURLを入力:
    https://seisan.example.com/webhook
    
  8. 「更新」ボタンをクリック
  9. 「検証」ボタンをクリック

成功メッセージ:

成功しました

エラーが出た場合:

  • 接続できませんでした → Nginxの設定を確認、バックエンドが起動しているか確認
  • 無効なHTTPステータスコード → アプリ側で200を返しているか確認

バス情報の設定

  1. 同様にLINE Developersコンソールで「バス情報」のチャネルを開く
  2. Webhook URLを変更:
    https://bus.example.com/callback
    
    ⚠️ /callback であることに注意(アプリによってエンドポイントが異なる)
  3. 「更新」→「検証」

6-4. 実際にLINEから動作確認

精算くんの確認:

  1. LINEアプリで精算くんのトークを開く
  2. 何かメッセージを送信
  3. 返信があればOK

バス情報の確認:

  1. LINEアプリでバス情報のトークを開く
  2. バス停の情報をリクエスト
  3. 返信があればOK

6-5. ngrok停止

独自ドメインで正常動作することを確認したら、ngrokを停止します:

# ラズパイ上で実行
ps aux | grep ngrok | grep -v grep

プロセスIDをメモして停止:

kill <プロセスID>

確認:

ps aux | grep ngrok | grep -v grep
# 何も表示されなければOK

ngrokの自動起動設定がある場合は削除:

# cronを確認
crontab -l | grep ngrok

# もしngrokの起動コマンドがあれば削除
crontab -e
# 該当行を削除して保存

ステップ7: セキュリティ設定

7-1. ファイアウォール設定(ufw)

ufwとは?
Ubuntu/Debianで使える簡単なファイアウォールツール。不要なポートへのアクセスをブロックします。

ufwのインストール

# ラズパイ上で実行
sudo apt update
sudo apt install -y ufw

デフォルトポリシーの設定

# 基本方針: 受信はすべて拒否、送信はすべて許可
sudo ufw default deny incoming
sudo ufw default allow outgoing

意味:

  • deny incoming: 外部からの接続を拒否(ホワイトリスト方式)
  • allow outgoing: ラズパイから外部への接続は許可

必要なポートのみ許可

# SSH(22番ポート)を許可
sudo ufw allow 22/tcp comment 'SSH'

# HTTP(80番ポート)を許可
sudo ufw allow 80/tcp comment 'HTTP'

# HTTPS(443番ポート)を許可
sudo ufw allow 443/tcp comment 'HTTPS'

なぜHTTPも許可?

  • HTTPSへのリダイレクトに使用
  • SSL証明書の更新(HTTP Challenge)に必要

ファイアウォール有効化

sudo ufw enable

警告メッセージが表示されます:

Command may disrupt existing ssh connections. Proceed with operation (y|n)?

y を入力してEnter

⚠️ SSH接続が切れるリスクがあります

  • ただし、ポート22を許可しているので通常は問題ありません
  • もし接続が切れたら、再度 ssh raspi で接続してください

設定確認

sudo ufw status verbose

出力例:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere                   # SSH
80/tcp                     ALLOW IN    Anywhere                   # HTTP
443/tcp                    ALLOW IN    Anywhere                   # HTTPS
22/tcp (v6)                ALLOW IN    Anywhere (v6)              # SSH
80/tcp (v6)                ALLOW IN    Anywhere (v6)              # HTTP
443/tcp (v6)               ALLOW IN    Anywhere (v6)              # HTTPS

IPv6版のルールも自動的に追加されます


外部から動作確認

# Macのターミナルから実行
curl -I https://www.example.com
# 正常に返ってくればOK

7-2. ルーター側のセキュリティ設定

二重のセキュリティ対策を実施します:

  1. ルーター側(Deco): ネットワークレベルでの防御
  2. サーバー側(ufw): ポートレベルでの防御

TP-Link Decoのセキュリティ機能を有効化

手順:

  1. Decoアプリを開く
  2. 「その他」→「詳細設定」→「セキュリティ」
  3. 以下3つをすべてONにする:

① 悪意あるコンテンツフィルタ

  • フィッシングサイトやマルウェア配布サイトへのアクセスをブロック
  • 影響: ほとんどなし(誤検知は稀)

② 侵入防止システム (IPS)

  • 外部からの不正アクセス試行を検知・ブロック
  • 影響: 正常なHTTPS通信が誤検知される可能性(低い)

③ 感染したデバイスの隔離

  • マルウェア感染が疑われるデバイスを自動で隔離
  • 影響: 誤検知でラズパイが隔離される可能性(低い)

動作確認

ONにした後、すぐに確認:

# Macのターミナルから実行
curl -I https://www.example.com
curl -I https://seisan.example.com
curl -I https://bus.example.com

すべて正常に返ってくればOK

もし誤検知でブロックされた場合:

  • Decoアプリ → 「セキュリティ」→「ブロックされた接続」で確認
  • 該当の機能を一時的にOFFにして原因を特定

7-3. 定期的なセキュリティメンテナンス

システムアップデート(月1回推奨)

ssh raspi
sudo apt update
sudo apt upgrade -y

Nginxのアクセスログで監視

# リアルタイムでアクセスログを監視
sudo tail -f /var/log/nginx/access.log

注目すべきログ:

  • 404 が大量: ポートスキャンの可能性
  • 知らないIPから大量アクセス: 攻撃の可能性

ステップ8: SSL証明書の自動更新設定

8-1. acme.shの自動更新確認

acme.shは自動的にcronに更新タスクを追加します:

crontab -l | grep acme

出力例:

28 0 * * * "/home/myuser/.acme.sh"/acme.sh --cron --home "/home/myuser/.acme.sh" > /dev/null

意味:

  • 毎日午前0時28分に証明書の更新チェックを実行
  • 証明書が期限切れ間近(30日以内)なら自動更新

8-2. 手動更新テスト

実際に更新できるか確認:

~/.acme.sh/acme.sh --renew -d example.com -d "*.example.com" --force

--force オプション:

  • 期限に関係なく強制的に更新
  • テスト用(本番では不要)

成功例:

[Mon Mar 22 16:00:00 JST 2026] Renew: 'example.com'
[Mon Mar 22 16:00:05 JST 2026] Renew success.

8-3. Nginx自動リロードの設定

証明書が更新されたらNginxを自動リロードする設定:

~/.acme.sh/acme.sh --install-cert -d example.com \
  --cert-file /etc/nginx/ssl/example.com.cer \
  --key-file /etc/nginx/ssl/example.com.key \
  --fullchain-file /etc/nginx/ssl/fullchain.cer \
  --reloadcmd "sudo systemctl reload nginx"

意味:

  • 証明書が更新されたら、自動的に /etc/nginx/ssl/ にコピー
  • その後、Nginxをリロード

⚠️ sudoのパスワードなし実行が必要:

# sudoersファイルを編集
sudo visudo

以下の行を最後に追加:

myuser ALL=(ALL) NOPASSWD: /bin/systemctl reload nginx

保存して終了: Ctrl + XY → Enter


8-4. TXTレコードの削除(オプション)

証明書取得が完了したら、TXTレコードは削除してOKです:

  1. お名前.com管理画面 → DNS設定
  2. _acme-challenge のTXTレコード2つを削除
  3. 保存

注意:

  • 削除しても問題ありません(更新時は再度追加します)
  • 残しておいても害はありません

トラブルシューティング

問題1: HTTPSでアクセスできない

症状

curl https://www.example.com
# curl: (35) error:0A000410:SSL routines::sslv3 alert handshake failure

原因と対策

原因 確認方法 対策
証明書のパーミッション不正 ls -la /etc/nginx/ssl/ sudo chmod 600 /etc/nginx/ssl/*.key
証明書のパスが間違っている sudo nginx -t 設定ファイルのパスを確認
ポート443が閉じている sudo lsof -i :443 sudo ufw allow 443/tcp
Nginxが起動していない sudo systemctl status nginx sudo systemctl start nginx

詳細確認方法

# SSL証明書の有効期限を確認
openssl x509 -in /etc/nginx/ssl/example.com.cer -noout -dates

# Nginxのエラーログを確認
sudo tail -50 /var/log/nginx/error.log

# ポート443が開いているか確認
sudo netstat -tlnp | grep 443

問題2: DNS Challenge が失敗する

症状

Verify error: Fetching http://.../.well-known/acme-challenge/...

原因

TXTレコードが浸透していない、または設定が間違っている

対策手順

1. TXTレコードの確認:

dig _acme-challenge.example.com TXT @8.8.8.8

2. 複数のDNSサーバーで確認:

dig _acme-challenge.example.com TXT @8.8.8.8    # Google
dig _acme-challenge.example.com TXT @1.1.1.1    # Cloudflare
dig _acme-challenge.example.com TXT @208.67.222.222  # OpenDNS

3. お名前.comの設定画面で再確認:

  • ホスト名が _acme-challenge であること
  • TYPEが TXT であること
  • VALUEにシングルクォート ' が入っていないこと

4. 浸透まで待機(最大1時間)


問題3: LINE Bot Webhookが動作しない

症状

LINE Developersの検証で「接続できません」「タイムアウト」

原因と対策

原因 確認方法 対策
エンドポイントのパス間違い アプリのコードを確認 /webhook/callback か確認
バックエンドが起動していない ps aux | grep node サービスを起動
Nginxのproxy_pass設定ミス Nginx設定ファイルを確認 proxy_passのポート番号確認
HTTPSの設定ミス curl -I https://seisan.example.com HTTPS動作確認

詳細確認方法

1. ローカルでWebhookをテスト:

# ラズパイ上で実行
curl -X POST http://localhost:3000/webhook \
  -H 'Content-Type: application/json' \
  -d '{"events":[]}'
# 200 OKが返ってくればバックエンドは正常

2. Nginxのアクセスログを確認:

sudo tail -f /var/log/nginx/access.log
# LINE Developers から検証ボタンを押す
# ログに接続が記録されるか確認

問題4: iPhoneやMacから接続できない

症状

ドメインでアクセスすると「お名前.comのパーキングページ」が表示される

原因1: /etc/hosts にローカル設定が残っている

Macの場合:

# hostsファイルを確認
cat /etc/hosts | grep example

# 不要な行があれば削除
sudo nano /etc/hosts
# 該当行を削除して保存

# DNSキャッシュをクリア
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

iPhoneの場合:

  • 設定 → Wi-Fi → 接続中のネットワークの (i) マーク → 「DHCPリースを更新」

原因2: DNS浸透中

確認:

nslookup www.example.com

対策:

  • 数時間待つ
  • または、DNSサーバーを手動で 8.8.8.8 に変更

問題5: ファイアウォールでブロックされている

症状

curl https://www.example.com がタイムアウト

確認方法

# ufwの状態確認
sudo ufw status verbose

# 特定のポートが許可されているか確認
sudo ufw status | grep 443

対策

# ポート443を許可(もし許可されていなければ)
sudo ufw allow 443/tcp

# ufwをリロード
sudo ufw reload

# 確認
sudo ufw status

まとめ

お疲れさまでした!これで以下がすべて完成しました:

✅ 完成したこと

  • 独自ドメインでの自宅サーバー公開
  • ワイルドカード証明書によるHTTPS化(すべてのサブドメインで共通)
  • Nginxリバースプロキシで複数サービスを管理
  • LINE BotのWebhook接続(ngrok不要)
  • ファイアウォールによる二重のセキュリティ対策
  • SSL証明書の自動更新(90日ごと)

📊 アーキテクチャ図

インターネット
    ↓
[お名前.com DNS]
    ↓ (CNAME → DDNS)
[ルーター]
    ├─ DDNS (yourname.tplinkdns.com)
    ├─ ポートフォワーディング (80, 443)
    └─ セキュリティ機能 (IPS, 悪意あるコンテンツフィルタ)
    ↓
[Raspberry Pi (192.168.x.x)]
    ├─ ufw ファイアウォール (22, 80, 443のみ許可)
    ├─ Nginx (リバースプロキシ)
    │   ├─ www.example.com → :5173 (Vite) + :3001 (Backend)
    │   ├─ seisan.example.com → :3000 (LINE Bot)
    │   └─ bus.example.com → :5000 (LINE Bot)
    └─ acme.sh (SSL証明書自動更新)

今後の改善案

1. systemdでサービス自動起動

現状の課題:

  • ラズパイを再起動すると、手動でアプリを起動する必要がある

解決策:

sudo nano /etc/systemd/system/myapp-backend.service
[Unit]
Description=My Backend Service
After=network.target

[Service]
Type=simple
User=myuser
WorkingDirectory=/home/myuser/myapp/backend
ExecStart=/usr/bin/node src/server.js
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl enable myapp-backend
sudo systemctl start myapp-backend

2. fail2ban導入

目的:

  • 不正なSSHログイン試行を自動ブロック

インストール:

sudo apt install -y fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

3. 監視ツール導入

無料で使えるサービス:

  • UptimeRobot: サイトの死活監視(5分ごとにpingを送る)
  • Prometheus + Grafana: サーバーのリソース監視

4. バックアップ自動化

重要なデータ:

  • データベース
  • Nginx設定ファイル
  • SSL証明書

バックアップスクリプト例:

#!/bin/bash
DATE=$(date +%Y%m%d)
tar -czf /backup/nginx-$DATE.tar.gz /etc/nginx/
tar -czf /backup/ssl-$DATE.tar.gz /etc/nginx/ssl/

参考リンク


おわりに

この記事では、Raspberry Piを使った自宅サーバーの構築を、初心者の方でも再現できるよう詳しく解説しました。

Claude Codeに頼らず、自分の手で構築できるようになることを目指して、すべての手順に理由と確認方法を記載しました。

もし不明点やエラーが発生した場合は、コメント欄でお気軽にご質問ください。一緒に解決しましょう!

Happy Hacking! 🚀

9
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?