Chuyển hosting là lúc dễ mất dữ liệu và dễ để web chết vài tiếng. Cách an toàn là dựng site đầy đủ trên máy chủ mới, kiểm thử kỹ khi DNS vẫn trỏ về máy cũ, rồi mới đổi DNS. Người dùng sẽ không thấy gián đoạn. Bài này đi qua toàn bộ quy trình bằng WP-CLI và rsync, áp dụng cho chuyển từ hosting sang VPS hoặc giữa hai VPS.

Bạn cần chuẩn bị:

  • Quyền SSH vào máy chủ cũ và mới. Nếu hosting cũ không có SSH, bạn có thể tải file qua SFTP và xuất database bằng phpMyAdmin.
  • Máy chủ mới đã cài sẵn web server, PHP, MariaDB, cùng phiên bản PHP tương thích với site.
  • Quyền sửa DNS của tên miền example.com.
  • WP-CLI trên cả hai máy (nếu chưa có, xem bước 2).

Trong bài, máy cũ là old.server, máy mới có IP 203.0.113.10.

Bước 1: Hạ TTL của bản ghi DNS

Làm việc này trước ngày chuyển ít nhất bằng TTL hiện tại. Ví dụ TTL đang 86400 giây thì hạ trước 1 ngày. Đặt TTL bản ghi A của @ và www xuống 300 giây. Kiểm tra:

dig example.com A +noall +answer

TTL thấp giúp khi đổi IP, người dùng được chuyển sang máy mới trong vài phút, và bạn có thể quay lại máy cũ nhanh nếu cần.

Bước 2: Cài WP-CLI

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp

Kiểm tra trong thư mục site cũ:

cd /var/www/example.com
wp core version
wp option get siteurl

Ghi lại siteurl để so sánh sau.

Bước 3: Sao lưu database trên máy cũ

cd /var/www/example.com
wp db export ~/example-$(date +%F).sql --single-transaction --quick
ls -lh ~/example-*.sql

Tùy chọn --single-transaction giúp xuất dữ liệu nhất quán với bảng InnoDB mà không khóa site. Kiểm tra charset của database để tạo đúng trên máy mới:

wp db query "SELECT @@character_set_database, @@collation_database;"

Bước 4: Chép file sang máy mới bằng rsync

Chạy trên máy mới, kéo file từ máy cũ về. rsync chỉ truyền phần thay đổi, nên bạn có thể chạy lại nhiều lần.

sudo mkdir -p /var/www/example.com
sudo chown deploy:deploy /var/www/example.com
rsync -avz --progress deploy@old.server:/var/www/example.com/ /var/www/example.com/
rsync -avz deploy@old.server:~/example-*.sql ~/

Dấu / ở cuối đường dẫn nguồn nghĩa là chép nội dung bên trong, không tạo thêm thư mục con. Nếu muốn bỏ cache plugin để giảm dung lượng, thêm --exclude 'wp-content/cache/'.

Bước 5: Tạo database và import trên máy mới

sudo mariadb -e "CREATE DATABASE wp_example CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mariadb -e "CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'MatKhauMoi_2026';"
sudo mariadb -e "GRANT ALL PRIVILEGES ON wp_example.* TO 'wp_user'@'localhost';"

Mở wp-config.php trên máy mới và cập nhật DB_NAME, DB_USER, DB_PASSWORD, DB_HOST nếu khác máy cũ. Sau đó import:

cd /var/www/example.com
wp db import ~/example-2026-10-23.sql
wp option get siteurl
sudo chown -R www-data:www-data /var/www/example.com

Nếu siteurl trùng với máy cũ, dữ liệu đã đúng. Chỉ khi đổi tên miền bạn mới cần chạy:

wp search-replace 'https://old-example.com' 'https://example.com' --all-tables --precise --dry-run

Xem số dòng thay đổi, rồi chạy lại không có --dry-run. Không dùng lệnh sed trên file SQL, vì nó làm hỏng dữ liệu serialize của WordPress.

Bước 6: Kiểm thử máy mới bằng file hosts

File hosts giúp máy tính của bạn trỏ example.com về IP mới, trong khi mọi người khác vẫn vào máy cũ. Thêm dòng sau:

203.0.113.10 example.com www.example.com
  • Linux, macOS: sudo nano /etc/hosts
  • Windows: mở Notepad bằng quyền Administrator, sửa C:\Windows\System32\drivers\etc\hosts

Bạn cũng có thể kiểm tra nhanh bằng curl mà không sửa hosts:

curl -I --resolve example.com:443:203.0.113.10 https://example.com/

Nếu chưa có SSL trên máy mới, Certbot với HTTP-01 chưa chạy được vì DNS chưa trỏ về. Bạn có thể tạm chép chứng chỉ từ máy cũ, dùng xác thực DNS-01, hoặc kiểm thử qua HTTP rồi cài SSL ngay sau khi đổi DNS.

Danh sách cần kiểm tra trên máy mới:

  • Trang chủ, bài viết, trang danh mục, tìm kiếm.
  • Đăng nhập /wp-admin, upload thử một ảnh.
  • Form liên hệ gửi được email.
  • Giỏ hàng, thanh toán (nếu có WooCommerce), dùng chế độ thử nghiệm.
  • Log lỗi: sudo tail -f /var/log/nginx/example.com.error.log.

Kiểm tra xong, nhớ xóa dòng vừa thêm trong file hosts.

Bước 7: Đồng bộ lần cuối và đổi DNS

Nếu site có dữ liệu mới liên tục (bình luận, đơn hàng), hãy đóng băng ngắn: tạm khóa bình luận hoặc bật bảo trì cho trang thanh toán trên máy cũ. Sau đó đồng bộ lần cuối:

# Trên máy cũ
cd /var/www/example.com && wp db export ~/final.sql --single-transaction --quick

# Trên máy mới
rsync -avz deploy@old.server:/var/www/example.com/wp-content/uploads/ /var/www/example.com/wp-content/uploads/
rsync -avz deploy@old.server:~/final.sql ~/
cd /var/www/example.com && wp db import ~/final.sql
sudo chown -R www-data:www-data /var/www/example.com

Ngay sau đó, sửa bản ghi A của @ và www sang 203.0.113.10. Không động vào MX và TXT nếu email không chuyển.

Bước 8: Sau khi đổi DNS

dig +short example.com @1.1.1.1
dig +short example.com @8.8.8.8
sudo tail -f /var/log/nginx/example.com.access.log

Khi access log máy mới có truy cập đều, cài SSL (nếu chưa), xóa cache plugin, bật lại bình luận. Theo dõi access log máy cũ: khi gần như không còn truy cập, bạn có thể tăng TTL trở lại 3600. Giữ máy cũ thêm vài ngày làm đường lùi.

Kiểm tra lại

  • wp core verify-checksums không báo file lõi bị sai lệch.
  • wp cron event list có các tác vụ định kỳ. Nếu đã tắt WP-Cron, thêm cron hệ thống trên máy mới.
  • Google Search Console không báo tăng lỗi thu thập dữ liệu trong những ngày sau.
  • Email từ website (đặt hàng, quên mật khẩu) vẫn tới hộp thư.

Lỗi thường gặp và cách xử lý

Trang trắng hoặc lỗi 500 trên máy mới

Thường do thiếu extension PHP hoặc phiên bản PHP không tương thích plugin. Bật log: thêm define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', true ); vào wp-config.php, rồi đọc wp-content/debug.log. Tắt debug khi xong.

Ảnh không hiển thị

Thư mục wp-content/uploads chưa chép đủ, hoặc sai quyền. Chạy lại rsync và chown.

Bị chuyển hướng về tên miền cũ hoặc vòng lặp redirect

Kiểm tra wp option get home và siteurl. Nếu site chạy sau proxy như Cloudflare, xem chế độ SSL có đặt Flexible không. Chế độ này hay gây vòng lặp với WordPress đã ép HTTPS.

Lỗi "Unknown collation utf8mb4_unicode_ci" khi import

File SQL xuất từ MySQL 8 sang MariaDB. Xuất lại với collation tương thích, hoặc thay collation trong file dump bằng utf8mb4_unicode_ci trước khi import. Thay chuỗi collation không ảnh hưởng dữ liệu serialize.