Nếu bạn đang tự vận hành Home Server hay NAS tại nhà, nhưng IP public không cố định, việc truy cập từ xa sẽ gặp khó khăn. Giải pháp hiệu quả là dùng Cloudflare để cập nhật IP động cho domain của bạn, tương tự như một dịch vụ DDNS nhưng ổn định, bảo mật, miễn phí và chuyên nghiệp hơn.

1. Yêu cầu hệ thống
- Một domain được quản lý qua Cloudflare
- Hệ thống server tại nhà (Ubuntu, Debian, Raspberry Pi, v.v.)
- Đã mở port truy cập từ xa hoặc cấu hình reverse proxy
- Công cụ
jq
vàcurl
cài sẵn
Lưu ý: Tên miền sử dụng phải là loại trả phí (.com, .net, .org, .me, …), không thể sử dụng các loại tên miền miễn phí (.tk, .ga, .ml, .cf, .gq, …) vì Cloudflare không hỗ trợ.
2. Tìm Zone ID và tạo API Token
Xem hướng dẫn cách lấy Zone ID và tạo API Token ở đây
3. Cài đặt Script tự động cập nhật IP
Đầu tiên bạn login SSH vào máy chủ của bạn để thiết lập bảng danh sách và script
3.1 Tạo file chứa danh sách domain
Trong bài này mình hướng dẫn cập nhật 1 hoặc nhiều domain cùng một lúc, vì vậy mình sẽ khởi tạo 1 file TXT chứa danh sách các domain cần cập nhật theo cấu trúc định sẵn như sau
File records.txt
mỗi dòng:zone_id|record_name|api_token|true/false
Đây là file mẫu của mình cho các bạn xem nhé
9a6ef568c2b7b89c251d08574aad4f61|bachtran.net|Nrj9iYBGKDDH1QNeVPGjhmUbooTYgjM-VK53Vo0d|true
87bb8614604e1253a6ea5c57bdc2fb16|domain1.com|mdYNtzb3-QATbaoa18c1BJbRUEYhtf2bqa7etRM4|true
48ba42390521fa214c93fa00eb1d2beb|domain2.com|E1a6R_vWkV6fDo5VnTw0efO2z85_8VKKL1bo6DOt|true
Giải thích:
Zone_ID: lấy trên cloudflare
Record_name: thường là tên domain của bạn
API_token: Tạo API cho điều chỉnh riêng từng domain
True/False: chỉ điền 1 trong 2. Nếu true thì domain của bạn sẽ qua proxy, nếu chọn false thì nó sẽ không qua proxy.
Sử dụng lệnh
nano records.txt
Nên soạn thảo một file ở dưới máy tính của mình để copy dán vào nano cho tiện sau đó lưu lại file.
3.2 Tạo Cloudflare.sh script
Dùng lệnh sau đây để tạo file Cloudflare.sh
nano Cloudflare.sh
Nhập vào nội dung sau đây.
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RECORDS_FILE="$SCRIPT_DIR/records.txt"
if ! command -v jq &> /dev/null; then
echo "❌ Lỗi: jq chưa được cài. Vui lòng cài bằng: sudo apt install jq"
exit 1
fi
if [[ ! -f "$RECORDS_FILE" ]]; then
echo "❌ File $RECORDS_FILE không tồn tại!"
exit 1
fi
echo "🚀 Đang lấy địa chỉ IP hiện tại..."
ip=$(curl -s4 https://icanhazip.com/)
if [[ -z "$ip" ]]; then
>&2 echo "❌ Không thể lấy địa chỉ IP public."
exit 1
fi
echo "🌐 IP hiện tại: ${ip}"
echo "🔍 Kiểm tra IP cũ của các record..."
all_same_ip=true
while IFS='|' read -r zone_id record_name api_token proxied; do
response=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records?name=${record_name}&type=A" \
-H "Authorization: Bearer ${api_token}" \
-H "Content-Type: application/json")
old_ip=$(echo "$response" | jq -r '.result[0].content')
if [[ "$old_ip" != "$ip" ]]; then
all_same_ip=false
break
fi
sleep 0.2
done < "$RECORDS_FILE"
if $all_same_ip; then
echo "✅ Tất cả bản ghi đã trùng với IP hiện tại ($ip), không cần cập nhật."
exit 0
fi
echo "♻️ Phát hiện có sự thay đổi IP. Tiến hành cập nhật..."
total_records=$(wc -l < "$RECORDS_FILE")
record_count=0
updated_count=0
unchanged_count=0
error_count=0
proxy_count=0
no_proxy_count=0
processing_results=()
show_progress() {
local percentage=$1
local bar_length=40
local filled_length=$((percentage * bar_length / 100))
local empty_length=$((bar_length - filled_length))
local filled_bar=$(printf "%${filled_length}s" | tr ' ' '=')
local empty_bar=$(printf "%${empty_length}s" | tr ' ' '-')
printf "\r➡️ Đang xử lý: [%s%s] %d%%" "$filled_bar" "$empty_bar" "$percentage"
}
while IFS='|' read -r zone_id record_name api_token proxied; do
((record_count++))
percentage=$((record_count * 100 / total_records))
show_progress "$percentage"
# Xử lý giá trị proxied (chuyển sang true/false kiểu bool)
case "${proxied,,}" in
true) proxied_bool=true; ((proxy_count++));;
false) proxied_bool=false; ((no_proxy_count++));;
*) proxied_bool=false; ((no_proxy_count++));;
esac
result="$record_name - "
response=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records?name=${record_name}&type=A" \
-H "Authorization: Bearer ${api_token}" \
-H "Content-Type: application/json")
record_id=$(echo "$response" | jq -r '.result[0].id')
old_ip=$(echo "$response" | jq -r '.result[0].content')
if [[ "$record_id" == "null" || -z "$record_id" ]]; then
result+="❌ Không tìm thấy record hoặc Zone ID sai"
((error_count++))
elif [[ "$old_ip" == "$ip" ]]; then
result+="✅ Không thay đổi"
((unchanged_count++))
else
update_payload=$(jq -n \
--arg type "A" \
--arg name "$record_name" \
--arg content "$ip" \
--argjson ttl 120 \
--argjson proxied "$proxied_bool" \
'{type: $type, name: $name, content: $content, ttl: $ttl, proxied: $proxied}')
update_result=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records/${record_id}" \
-H "Authorization: Bearer ${api_token}" \
-H "Content-Type: application/json" \
--data "$update_payload")
if echo "$update_result" | grep -q '"success":true'; then
result+="✅ Cập nhật thành công: $old_ip → $ip"
((updated_count++))
else
result+="❌ Lỗi cập nhật"
((error_count++))
fi
fi
processing_results+=("$result")
sleep 0.2
done < "$RECORDS_FILE"
echo ""
echo -e "\n📋 Kết quả chi tiết:"
for item in "${processing_results[@]}"; do
echo " └── $item"
done
echo -e "\n✅ Hoàn tất. Tổng: $total_records | Cập nhật: $updated_count | Không đổi: $unchanged_count | Lỗi: $error_count"
echo -e "🔶 Qua proxy: $proxy_count | Không qua proxy: $no_proxy_count"
echo -e "by bachtran.net"
Sau đó lưu lại. File Cloudflare.sh
và records.txt
nên ở cùng một thư mục nhé.
Sẵn đây cấp quyền thực thi cho nó luôn
chmod +x cloudflare.sh
Câu lệnh chạy thử nó
./cloudflare.sh
Khi bạn chạy thử thành công nó sẽ hiển thị các thông tin ra như thế này

4. Thiết lập Cron tự động
Để việc này tự động hoàn toàn thì bạn thiết lập cron cho nó tự chạy mỗi 5 phút chẳng hạn
crontab -e
Thêm dòng này vào dưới cùng cron để chạy mỗi 5 phút
*/5 * * * * /bin/bash /home/bachtran/cloudflare.sh
Chú ý thay đổi thành đường dẫn chính xác của riêng bạn /home/bachtran/cloudflare.sh
Chúc thành công !