2026 免費 DDNS 教學:GCP+Cloudflare+Draytek 零成本實作

DDNS 運作原理圖,展示家中路由器如何透過動態 DNS 更新 Cloudflare 紀錄

目錄

☁️ 2026 DDNS 方案懶人包

浮動 IP 救星!為什麼你需要捨棄傳統付費服務,擁抱 Cloudflare + GCP?

比較項目傳統 DDNS (No-IP)Cloudflare + GCP (本篇推薦)
費用免費版需每月續期 / 付費貴永久免費
網域名稱醜陋的子網域 (ddns.net)自訂頂級網域 (.com/.net)
安全性容易暴露真實 IPCloudflare Proxy 隱藏 IP
生效速度較慢 (TTL 長)極速 (秒級更新)
👇 詳細架設步驟請往下閱讀 👇

什麼是 DDNS?為什麼你需要它?

DDNS (動態網域名稱系統) 是一種將浮動 IP 自動對應到固定網域名稱的技術,透過客戶端軟體即時更新 DNS 紀錄,適合 NAS 架設、遠端桌面與 VPN 用戶在沒有固定 IP 時使用。

對於大多數的家用寬頻用戶來說,ISP (如中華電信) 提供的通常是「浮動 IP」,這意味著你的公網 IP 位址會不定期變動。當你在外面想要連線回家的 NAS 存取檔案,或是連線到架設在樹莓派上的 Home Assistant 時,IP 一旦變動,連線就會中斷。

這就是 DDNS 的核心價值。它就像是一個隨身秘書,隨時監控你家中的 IP 變化,一旦發現 IP 改變,就會立即通知 DNS 伺服器更新紀錄。我們在克隆資訊實驗室長期測試發現,穩定的 DDNS 是自架服務的基石,而選擇正確的服務商更是關鍵。

您可以將網域名稱 (例如 my-office.com) 想像成一個聯絡人的名字,而 IP 位址 (例如 114.33.22.11) 則是他的手機號碼。傳統的 DNS 就像一本靜態的電話簿,您手動將名字對應到號碼。但問題是,大多數家庭或小型辦公室的網路,其 IP 位址是由電信商動態分配的,就像手機號碼會變一樣。

DDNS (Dynamic DNS) 就是一位聰明的、全天候的通訊錄管家。當它發現您的 IP 位址變更時,會自動更新您的「電話簿」,確保別人透過您的名字 (my-office.com),永遠都能找到您最新的號碼 (最新的 IP 位址)。

為何選擇 GCP 免費 VM + Cloudflare?

  • GCP 永久免費 VM (虛擬主機):Google Cloud 提供了一個「Always Free」方案,其中包含一台 e2-micro 等級的虛擬主機,其資源足以 24 小時不間斷地運行我們的輕量級 DDNS 服務,且完全免費。這讓我們擁有了一個穩定的伺服器基礎。
  • Cloudflare DNS API:Cloudflare 不僅是頂級的 CDN 和 DNS 服務商,更提供了強大且免費的 API。我們可以透過程式化呼叫這些 API,來精準、即時地更新我們的 DNS 紀錄,這正是實現 DDNS 的核心。

結合這兩者,我們就能以零成本打造出比許多付費服務更具彈性、更安全且完全在自己掌控之下的 DDNS 系統。

系統架構總覽

由於我們的 GCP 伺服器本身也可能因為重啟而獲得新的動態 IP,所以我們將建立一個雙層的動態更新系統,確保服務永不中斷。

  1. GCP VM 會透過排程任務,定期檢查自己的 IP,並更新 ddns-server.yourdomain.com。
  2. Draytek 路由器 會將 ddns-server.yourdomain.com 作為伺服器位址,將自己的 IP 更新請求發送過去。

前置準備

在開始之前,請確保您已擁有:

  1. 一個 Google 帳號 (用來註冊 GCP)。
  2. 一個 Cloudflare 帳號。
  3. 一個已轉移至 Cloudflare 管理的有效網域名稱 (例如 yourdomain.com)。

DDN 架設與 Cloudflare 設定

DDN 架設 的首要步驟是取得 Cloudflare API Token。進入 Cloudflare 後台,建立一個具備 Zone.DNS 編輯權限的 Token,並獲取 Zone ID,這是後續設定路由器或腳本進行自動更新的必要憑證。

以下是我們在實驗室標準化的設定流程,請務必按步驟操作:

  1. 託管網域:將你的網域 DNS 伺服器指向 Cloudflare(NS 紀錄)。

  2. 建立 A 紀錄:在 DNS 設定頁面,新增一個 A 紀錄(例如 vpnnas),IP 先隨便填寫(如 1.1.1.1),Proxy 狀態建議先設為 “DNS Only” (灰色雲朵) 以確保直連測試成功。

  3. 獲取 API Token

    • 點擊右上角「設定檔」>「API 權杖」。

    • 選擇「建立 Token」> 使用「編輯區域 DNS (Edit zone DNS)」範本。

    • 權限設定:確保包含 區域 - DNS - 編輯

    • 資源範圍:選擇包含你的特定網域。

    • 產生並保存這串 Token(只會顯示一次,請妥善保管)。

這組 Token 將是你的設備(無論是路由器、NAS 或 GCP 腳本)與 Cloudflare 溝通的鑰匙。

免費 DDNS 架設 Cloudflare API 設定畫面,展示 API Token 權限配置細節,步驟1
登入 Cloudflare,前往 「設定檔」。
免費 DDNS 架設 Cloudflare API 設定畫面,展示 API Token 權限配置細節,步驟2
「API 權杖」>「建立 Token」。
免費 DDNS 架設 Cloudflare API 設定畫面,展示 API Token 權限配置細節,找到 「編輯區域 DNS」 (Edit zone DNS) 的範本,點擊 「使用範本」。
找到 「編輯區域 DNS」 (Edit zone DNS) 的範本,點擊 「使用範本」。
免費 DDNS 架設 Cloudflare API 設定畫面,權杖名稱:自訂一個易於識別的名稱,例如 : DDNS-Service。區域資源:選擇 包括 - 特定區域 - 您的根網域名稱
權杖名稱:自訂一個易於識別的名稱,例如 : DDNS-Service。區域資源:選擇 包括 - 特定區域 - 您的根網域名稱。
免費 DDNS 架設 Cloudflare API 設定畫面,Cloudflare API建立完成,立刻複製並安全地保存顯示出來的 API 權杖,它只會出現這一次
Cloudflare API建立完成

建立與設定 GCP 免費主機 VM

結合 GCP (Google Cloud Platform) 的永久免費 e2-micro 實例,可以打造一個獨立的 DDNS 監控中心。透過在 GCP 上執行 Python 腳本,能更精準地偵測真實 IP 變化,避免依賴路由器內建的不穩定功能。

詳細申請步驟請參考:GCP 免費 VPS 申請:永久免費 e2-micro 架站實戰

為什麼要將 GCP 納入這個架構?許多市售路由器(如 ASUS, Draytek)內建的 DDNS 功能更新速度較慢,且支援的服務商有限。

在克隆資訊的架構中,我們利用 Google Cloud Platform 的 us-west1us-central1 區域的 e2-micro 實例(符合 Always Free 資格)來運行自定義的 updater 腳本。

操作邏輯如下

  1. 申請 GCP VM:建立一台 Ubuntu 或 Debian 的 e2-micro 機器。

  2. 部署腳本:使用 Python 或 Docker 部署 cloudflare-ddns 容器。

  3. 驗證機制:讓 GCP 定時(透過 Crontab)向你的家中路由器發起請求,或由路由器主動回報 IP 給 GCP,再由 GCP 透過 API 統一更新 Cloudflare。

這種「三層式架構」(路由器 -> GCP -> Cloudflare)雖然聽起來複雜,但極大提升了穩定性與擴充性,特別適合擁有多個外網 IP 的進階玩家。

部署伺服器自我更新腳本 (Cron Job)

現在我們要連線到VM,並放上核心的Python更新腳本。

第一階段:環境準備與相依套件安裝

在開始部署腳本之前,我們需要先建立一個乾淨的 Python 虛擬環境,避免與系統環境衝突。

1. 連線至 VM 在 GCP VM 執行個體清單中,點擊您的 VM 右側的 「SSH」 按鈕開啟終端機。

GPC 免費主機主控台開啟 SSH 終端機
連線到VM:在VM執行個體清單中,找到您的VM,點擊右側的 「SSH」 按鈕。

2. 更新系統並安裝基礎軟體 複製並執行以下指令:

				
					sudo apt-get update
sudo apt-get install python3-venv python3-pip -y
				
			

3. 建立專案目錄與虛擬環境 我們會將所有檔案統一放在 ddns 資料夾中,方便管理:

				
					# 建立並進入資料夾
mkdir -p ~/ddns
cd ~/ddns

# 建立虛擬環境 (名稱為 ddns-env)
python3 -m venv ddns-env

# 啟動虛擬環境並安裝 requests 套件
source ddns-env/bin/activate
pip install requests
				
			

第二階段:部署伺服器自我更新腳本 (Cron Job)

此腳本用於更新 VM 本身 的 IP 位址到 Cloudflare (例如:ddns-server.domain.com)。

1. 建立腳本檔案

				
					nano ~/ddns/gcp_self_updater.py
				
			

2. 貼上程式碼

				
					import requests
import sys

# ==========================================
# [設定區] 請修改以下資訊
# ==========================================
CF_API_TOKEN = "貼上您從Cloudflare複製的API權杖"
ZONE_NAME = "domain.com"             # 您的根網域名稱
SERVER_HOSTNAME = "ddns-server.domain.com" # 這台伺服器要綁定的網址
# ==========================================

API_BASE_URL = "https://api.cloudflare.com/client/v4"
HEADERS = {
    "Authorization": f"Bearer {CF_API_TOKEN}",
    "Content-Type": "application/json"
}

def get_public_ip():
    """獲取目前的公網 IP"""
    try:
        response = requests.get('https://api.ipify.org?format=json', timeout=10)
        response.raise_for_status()
        return response.json()['ip']
    except requests.RequestException as e:
        raise Exception(f"無法獲取公網 IP: {e}")

def update_dns():
    print("--- 開始執行伺服器自我 IP 更新 (v5.1) ---")
    try:
        new_ip = get_public_ip()
        print(f"偵測到目前公網 IP: {new_ip}")

        # 1. 取得 Zone ID
        url = f"{API_BASE_URL}/zones"
        response = requests.get(url, headers=HEADERS, params={'name': ZONE_NAME})
        response.raise_for_status()
        result = response.json()['result']
        if not result:
            raise Exception(f"找不到網域 {ZONE_NAME}")
        zone_id = result[0]['id']
        
        # 2. 取得目前的 DNS 紀錄
        url = f"{API_BASE_URL}/zones/{zone_id}/dns_records"
        response = requests.get(url, headers=HEADERS, params={'name': SERVER_HOSTNAME, 'type': 'A'})
        response.raise_for_status()
        dns_records = response.json()['result']
        
        current_ip = dns_records[0]['content'] if dns_records else None
        
        if new_ip == current_ip:
            print(f"IP 未變更 ({new_ip}),無需更新。")
            return

        print(f"伺服器 IP 已從 {current_ip} 變為 {new_ip},準備更新...")
        
        # 3. 更新或建立紀錄 (強制 proxied: False 以確保直連)
        dns_record_data = {'type': 'A', 'name': SERVER_HOSTNAME, 'content': new_ip, 'ttl': 120, 'proxied': False}
        
        if dns_records:
            record_id = dns_records[0]['id']
            url = f"{API_BASE_URL}/zones/{zone_id}/dns_records/{record_id}"
            response = requests.put(url, headers=HEADERS, json=dns_record_data)
        else:
            url = f"{API_BASE_URL}/zones/{zone_id}/dns_records"
            response = requests.post(url, headers=HEADERS, json=dns_record_data)
        
        response.raise_for_status()
        if response.json()['success']:
            print(f"***** 伺服器 DNS 紀錄 '{SERVER_HOSTNAME}' 更新成功! *****")
        else:
            raise Exception(f"Cloudflare API 操作失敗: {response.json()['errors']}")

    except Exception as e:
        print(f"!!! 發生錯誤: {e} !!!", file=sys.stderr)

if __name__ == '__main__':
    update_dns()
    print("--- 執行完畢 ---")
				
			

按 Ctrl+O 儲存,Ctrl+X 離開。

3. 設定排程任務 (Crontab) 我們要讓這個腳本每分鐘檢查一次 IP。

				
					crontab -e
				
			

(如果是第一次執行,請選擇 1. /bin/nano)

在檔案最下方新增以下內容(請注意路徑,這裡假設您的使用者名稱是 clone,若不是請自行替換):

				
					# 每分鐘檢查一次並記錄 Log
* * * * * /home/clone/ddns/ddns-env/bin/python3 /home/clone/ddns/gcp_self_updater.py >> /home/clone/ddns/gcp_self_updater.log 2>&1
				
			

說明:我們使用絕對路徑指向虛擬環境中的 python3,確保相關套件能被正確載入。

第三階段:部署多設備 DDNS 伺服器 (Systemd Service)

此腳本會啟動一個 Web Server,接收來自路由器 (如 Draytek) 的更新請求。

1. 建立伺服器腳本

				
					nano ~/ddns/ddns_updater.py
				
			

2. 貼上程式碼

				
					import http.server
import socketserver
from urllib.parse import urlparse, parse_qs
import sys
import requests

# ==========================================
# [設定區] 請修改以下資訊
# ==========================================
PORT = 8080                          # 伺服器監聽埠號 (請記得在 GCP 防火牆開啟此埠號)
CF_API_TOKEN = "貼上您從Cloudflare複製的API權杖"
ZONE_NAME = "domain.com"             # 您的根網域名稱

# 定義允許更新的網址與對應密碼
ROUTER_SECRETS = {
    'office.domain.com': 'SuperSecretPasswordForOffice',
    'home.domain.com': 'AnotherPasswordForHome',
}
# ==========================================

API_BASE_URL = "https://api.cloudflare.com/client/v4"
HEADERS = {
    "Authorization": f"Bearer {CF_API_TOKEN}",
    "Content-Type": "application/json"
}

class DDNSHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        # 解析請求參數
        parsed_path = urlparse(self.path)
        params = parse_qs(parsed_path.query)
        
        # 獲取參數 (使用 get 防止出錯)
        hostname = params.get('hostname', [''])[0]
        new_ip = params.get('ip', [''])[0]
        secret = params.get('secret', [''])[0]

        # 1. 安全驗證
        if ROUTER_SECRETS.get(hostname) != secret:
            self.send_error_and_log(401, f"[{hostname}] 認證失敗:密碼錯誤或網址未授權")
            return
            
        print(f"[{hostname}] 認證成功,收到 IP: {new_ip}")

        try:
            # 2. 呼叫 Cloudflare API
            url = f"{API_BASE_URL}/zones"
            response = requests.get(url, headers=HEADERS, params={'name': ZONE_NAME})
            response.raise_for_status()
            result = response.json()['result']
            if not result:
                raise Exception(f"找不到 Zone: {ZONE_NAME}")
            zone_id = result[0]['id']

            url = f"{API_BASE_URL}/zones/{zone_id}/dns_records"
            response = requests.get(url, headers=HEADERS, params={'name': hostname, 'type': 'A'})
            response.raise_for_status()
            dns_records = response.json()['result']

            current_ip = dns_records[0]['content'] if dns_records else None
            
            if new_ip == current_ip:
                print(f"[{hostname}] IP 未變更 ({new_ip}),無需更新。")
                self.send_success_response("good", new_ip)
                return

            print(f"[{hostname}] IP 已從 {current_ip} 變為 {new_ip},準備更新...")
            
            # 強制 proxied: False (直連模式)
            dns_record_data = {'type': 'A', 'name': hostname, 'content': new_ip, 'ttl': 120, 'proxied': False}
            
            if dns_records:
                record_id = dns_records[0]['id']
                url = f"{API_BASE_URL}/zones/{zone_id}/dns_records/{record_id}"
                response = requests.put(url, headers=HEADERS, json=dns_record_data)
            else:
                url = f"{API_BASE_URL}/zones/{zone_id}/dns_records"
                response = requests.post(url, headers=HEADERS, json=dns_record_data)
            
            response.raise_for_status()
            if not response.json()['success']:
                 raise Exception(f"Cloudflare API 操作失敗: {response.json()['errors']}")
            
            print(f"***** [{hostname}] 操作成功!DDNS 更新完成! *****")
            self.send_success_response("good", new_ip)

        except Exception as e:
            self.send_error_and_log(500, f"!!! [{hostname}] 發生嚴重錯誤: {e} !!!")

    def send_success_response(self, status, ip):
        self.send_response(200)
        self.end_headers()
        self.wfile.write(bytes(f"{status} {ip}", "utf-8"))

    def send_error_and_log(self, code, message):
        print(message, file=sys.stderr)
        self.send_response(code)
        self.send_header('Content-type', 'text/plain; charset=utf-8')
        self.end_headers()
        self.wfile.write(message.encode('utf-8'))

    def log_message(self, format, *args):
        return # 關閉預設的 HTTP Log,使用我們自定義的輸出

if __name__ == "__main__":
    with socketserver.TCPServer(("", PORT), DDNSHandler) as httpd:
        print(f"多設備 DDNS 伺服器 (v5.1) 已在通訊埠 {PORT} 啟動...")
        print("請確保 GCP 防火牆已允許此通訊埠流入。")
        httpd.serve_forever()
				
			

3. 設定為系統服務 (Systemd) 這能確保您的伺服器在當機或重開機後會自動重啟。

				
					sudo nano /etc/systemd/system/ddns_updater.service
				
			

貼上以下內容 (請務必修改 User 和路徑,假設您的使用者名稱為 clone):

				
					[Unit]
Description=Cloudflare DDNS Updater Service
After=network.target

[Service]
# 請修改為您的使用者名稱
User=clone
Group=clone

# 設定工作目錄
WorkingDirectory=/home/clone/ddns/

# 設定環境變數確保 Log 即時輸出
Environment="PYTHONUNBUFFERED=1"

# 指向虛擬環境的 python3 以及腳本路徑
ExecStart=/home/clone/ddns/ddns-env/bin/python3 /home/clone/ddns/ddns_updater.py

# 當崩潰時自動重啟
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
				
			

3. 設定為系統服務 (Systemd) 這能確保您的伺服器在當機或重開機後會自動重啟。

				
					# 重新載入設定
sudo systemctl daemon-reload

# 啟動服務
sudo systemctl start ddns_updater.service

# 設定開機自動啟動
sudo systemctl enable ddns_updater.service

# 檢查狀態 (應顯示 active (running))
sudo systemctl status ddns_updater.service
				
			

設定 Draytek 路由器

這是將一切串連起來的最後一步。

  1. 登入您的 Draytek 路由器管理介面。
  2. 前往 「 Applications >> Dynamic DNS」,Service Provider 選擇 User-Defined。
  3. Provider Host:填寫您在 gcp_self_updater.py 中為伺服器設定的網域名稱和PORT,例如 ddns-server.domain.com:8080。
  4. Auth Type:Basic。
  5. Service API :依照以下格式填寫:
    /update?hostname=這台設備的A紀錄&ip=###IP###&secret=這台設備的密碼
    • 範例:/update?hostname=office.domain.com&ip=###IP###&secret=SuperSecretPasswordForOffice
  6. 儲存設定,並為您的每一台路由器重複此步驟。
免費 DDNS 成功連線畫面,顯示遠端存取與 IP 同步狀態
最終驗證:確保網域名稱正確解析至您的浮動 IP

🚀 結語:告別傳統,掌握流量自主權

透過本文的教學,您不僅省下了每年數十美金的傳統 DDNS 訂閱費,更掌握了基於 CloudflareGCP 的企業級 DNS 管理技術。這不僅僅是一次簡單的 DDNS 架設 過程,更是您邁向「自架服務 (Self-Hosted)」的重要里程碑。

在 2025 年,擁有一個穩定、快速且完全免費 DDNS 系統,意味著您可以隨時隨地安全地存取家中的 NAS、Home Assistant 或建立私有 VPN,不再受制於浮動 IP 的限制。

下一步該做什麼?

搞定 DDNS 後,雖然「大門」已經找到,但「路況」如何才是關鍵。建議您繼續閱讀這篇 網路環境與連線品質測試指南,學會如何專業地檢測線路頻寬與延遲,確保您的遠端連線不僅連得上,更要跑得快!

💡 常見問題

Q1:為什麼我的 Cloudflare DDNS 更新後無法連線?

A: 最常見的原因是開啟了 Proxy 模式(小橘雲)。若您是使用非 HTTP 協定(如 VPN、SSH、遠端桌面),必須將 Cloudflare DNS 紀錄設為「DNS Only」(灰色雲朵),否則連線會被 Cloudflare CDN 擋下。

Q2:使用 GCP 架設 DDNS 需要付費嗎?

A: 不需要。只要您選擇美西 (us-west1)、美中 (us-central1) 等指定區域,並使用標準永久磁碟 (Standard Persistent Disk) 與 e2-micro 機型,即符合 Google 的 Always Free 方案,可用於免費 DDNS 架設。

Q3:免費 DDNS 服務與付費版有什麼差別?

A: 傳統付費 DDNS 主要提供更短的 TTL 與自訂網域功能。但使用 Cloudflare 方案,您即可免費享有企業級的 API 更新速度與頂級網域支援,在 2025 年已無購買傳統付費 DDNS 的必要。

AI Overview Local Pack 衝擊實戰分析:排名沒掉、電話卻消失的 3 大真相

你的本地搜尋排名穩坐前三,來電卻莫名下滑 30% 以上?真相是 AI Overview Local Pack 正在取代傳統的地圖三件組。2025 年底起 Google 在美國行動搜尋大規模部署這項 AI 驅動新版面,Sterling Sky 分析 322 個市場發現,88% 的市場中新版面顯示的商家數量比傳統 3-Pack 更少,部分商家能見度暴跌超過 50%。

< SYSTEM_READY />

需要專業的服務?

無論是網頁設計、系統開發或 GCP 雲端服務,我們都能提供最適合您的解決方案。

// WAITING_FOR_INPUT...