Tới nội dung chính
hstation
Dịch vụDự ánVề tôiBlogThư viện nhạc

WordPress · 10 phút đọc · 2026-09-17

Chuyển hosting WordPress: đổi URL bằng wp search-replace

Sai một dấu giao thức hoặc tiền tố bảng là wp search-replace có thể sửa nhầm hoặc không sửa gì. Ba bẫy lớn nhất là dữ liệu serialize, cột guid và chuyển hướng còn sót từ domain cũ.

Chuyển hosting WordPress nghe như chép tệp và nhập cơ sở dữ liệu là xong. Phần khó nằm ở chỗ khác: địa chỉ cũ của site nằm rải rác khắp cơ sở dữ liệu và cả trong một tệp cấu hình, và mỗi chỗ sót lại hỏng theo một kiểu riêng.

Mình chép một site thử WordPress 7.1 từ http://localhost/wp-demo sang một bản mới ở http://localhost/wp-moi, với thư mục mới và cơ sở dữ liệu mới, rồi làm thử ba cách. Đổi đường dẫn trên cùng một máy mô phỏng đúng việc đổi địa chỉ site khi chuyển hosting. Chuyển kèm đổi tên miền WordPress cũng là đúng bài toán này: chuỗi địa chỉ cũ phải được thay bằng chuỗi mới ở mọi chỗ. Mọi con số dưới đây là kết quả chạy thật với Apache, MySQL 8.4 và WP-CLI 2.12.

Địa chỉ cũ nằm ở những đâu

Mình xuất cơ sở dữ liệu site cũ:

bash
wp db export db.sql

Tệp ra 1.072.006 byte. Trong đó, chuỗi localhost/wp-demo xuất hiện 20 lần:

  • 3 lần trong bảng wp_options, gồm siteurl, home, và 1 lần nằm trong dữ liệu serialize (ảnh đầu trang lưu trong theme_mods).
  • 5 lần trong nội dung bài viết.
  • 11 lần trong cột guid của bảng bài viết.
  • 1 lần trong hồ sơ người dùng.

Đó là lý do cách chuyển hosting WordPress nào chỉ sửa siteurl và home cũng mới đổi được 2 trên 20 chỗ.

Chuyển hosting WordPress: chuẩn bị bản mới

  1. Chép toàn bộ thư mục site sang chỗ mới. Trên cùng một máy, mình chép 4.050 tệp mất 32 giây. Chép qua mạng lên hosting thật sẽ lâu hơn nhiều.
  2. Tạo cơ sở dữ liệu mới, rồi sửa wp-config.php của bản mới cho trỏ vào đó:
bash
wp config set DB_NAME wpdemo_moi

Hosting mới cấp tài khoản cơ sở dữ liệu khác thì sửa thêm DB_USER, DB_PASSWORD, DB_HOST theo cùng cách.

Đã xong khi wp config get DB_NAME in ra đúng tên cơ sở dữ liệu mới.

Bẫy 1: nhập cơ sở dữ liệu nhưng quên đổi URL

Mình nhập nguyên db.sql vào bản mới, chưa đổi gì:

bash
wp db import db.sql
  • Trang chủ ở địa chỉ mới trả 404.
  • Link bài ở địa chỉ mới trả 301, chuyển hướng về địa chỉ cũ.
  • HTML trang chủ bản mới chứa 29 đường dẫn trỏ về địa chỉ cũ.
Trang chủ của bản chép sau khi nhập cơ sở dữ liệu mà quên đổi URL. Site Cửa hàng mẫu hiện trang Không tìm thấy trang: bên trái là ảnh đen trắng một cái cây nghiêng trong gió, bên phải là dòng chữ bắt đầu bằng Trang bạn đang tìm kiếm không tồn tại.
Trang chủ của bản chép sau khi nhập cơ sở dữ liệu mà quên đổi URL. Site Cửa hàng mẫu hiện trang Không tìm thấy trang: bên trái là ảnh đen trắng một cái cây nghiêng trong gió, bên phải là dòng chữ bắt đầu bằng Trang bạn đang tìm kiếm không tồn tại.

Nếu WordPress chuyển hướng về domain cũ ngay sau khi chuyển, hãy kiểm hai giá trị này trước tiên:

bash
wp option get siteurl
wp option get home

Bẫy 2: đổi URL bằng tìm-thay-thế chữ trên tệp SQL

Cách hay được nghĩ tới: mở tệp SQL, thay hết chuỗi cũ bằng chuỗi mới, rồi nhập. Mình làm đúng như vậy bằng sed:

bash
sed 's#localhost/wp-demo#localhost/wp-moi#g' db.sql > db-sed.sql

Kết quả sau khi nhập db-sed.sql:

  • Tệp mới không còn chuỗi cũ nào. siteurl và home đã đúng. Trang chủ 200.
  • Nhưng ảnh đầu trang lưu trong theme_mods mất trắng. Cả mục đó chỉ còn {"custom_css_post_id":-1}.

Nguyên nhân nằm ở cách PHP lưu dữ liệu serialize. Chuỗi được lưu kèm độ dài: s:68:"http://localhost/wp-demo/.... Sau khi thay, địa chỉ ngắn đi 1 ký tự nhưng con số vẫn là 68, nên PHP không đọc được nữa và coi như mục đó trống.

Không có thông báo lỗi nào trên trang. Ở site thử, thứ bị mất chỉ là ảnh đầu trang. Site thật có thể lưu nhiều thứ khác theo kiểu này; mình chưa đo trên plugin hay widget cụ thể. Nếu WordPress bị lỗi sau khi đổi URL theo kiểu "mất cài đặt mà không báo lỗi", đây là chỗ đáng nghi đầu tiên.

Cách đúng: wp search-replace

Nhập lại tệp db.sql gốc, chưa sửa, rồi chạy thử trước với --dry-run:

bash
wp db import db.sql
wp search-replace 'http://localhost/wp-demo' 'http://localhost/wp-moi' --dry-run

Lệnh báo trước 20 thay thế, chia theo bảng và cột:

  • wp_options / option_value: 3, kiểu PHP (nghĩa là lệnh tự xử lý đúng dữ liệu serialize).
  • wp_posts / post_content: 5.
  • wp_posts / guid: 11.
  • wp_users / user_url: 1.

Con số khớp với 20 lần đếm được ở đầu bài. Bỏ --dry-run để chạy thật:

bash
wp search-replace 'http://localhost/wp-demo' 'http://localhost/wp-moi'

Lệnh báo "Made 20 replacements" sau 407 ms. Ảnh đầu trang giờ đổi đúng sang địa chỉ mới, không mất gì.

Lệnh có cờ --skip-columns nếu bạn không muốn đụng tới một cột nào đó, ví dụ --skip-columns=guid. Thay địa chỉ cũ và mới bằng địa chỉ thật của bạn, và giữ nguyên phần http:// hoặc https:// cho khớp.

Bẫy 3: .htaccess chép theo vẫn trỏ về thư mục cũ

Sau search-replace, trang chủ 200, ảnh 200, nhưng link bài vẫn 301 về địa chỉ cũ. Mình mở tệp .htaccess của bản mới và thấy nó vẫn ghi:

text
RewriteBase /wp-demo/
RewriteRule . /wp-demo/index.php [L]

Tệp này được chép theo từ site cũ. Mọi đường dẫn bài đẹp đi qua nó, nên bị đẩy sang bản WordPress cũ, và bản cũ chuyển hướng về chính nó.

Mình thử hai cách sửa:

  1. `wp rewrite flush --hard` từ dòng lệnh: báo "Warning: Regenerating a .htaccess file requires special configuration" và tệp không đổi.
  2. Vào wp-admin, mở trang Cài đặt cấu trúc đường dẫn (wp-admin/options-permalink.php), không đổi lựa chọn nào, bấm nút lưu ở cuối trang: tệp được ghi lại thành RewriteBase /wp-moi/.
Trang Cài đặt cấu trúc đường dẫn trong wp-admin của bản chép ngay sau khi bấm lưu. Thông báo xanh ghi Cấu trúc đường dẫn cố định đã cập nhật. Lựa chọn đang chọn là Ngày và tên bài viết, và các ví dụ đường dẫn bên dưới đều bắt đầu bằng http://localhost/wp-moi/.
Trang Cài đặt cấu trúc đường dẫn trong wp-admin của bản chép ngay sau khi bấm lưu. Thông báo xanh ghi Cấu trúc đường dẫn cố định đã cập nhật. Lựa chọn đang chọn là Ngày và tên bài viết, và các ví dụ đường dẫn bên dưới đều bắt đầu bằng http://localhost/wp-moi/.

Sau đó link bài trả 200, ảnh 200, và HTML bài không còn đường dẫn nào trỏ về địa chỉ cũ.

Bẫy này chỉ xảy ra khi site nằm ở thư mục con khác sau khi chuyển. Nếu site nằm ở thư mục gốc cả trước lẫn sau thì RewriteBase vẫn là /; ca đó mình chưa đo.

Kiểm sau khi chuyển: bốn phép

  1. wp option get siteurl và wp option get home phải là địa chỉ mới.
  2. Trang chủ, một bài, một ảnh ở địa chỉ mới đều phải trả 200, không 301 về địa chỉ cũ:
bash
curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" https://dia-chi-moi/duong-dan-bai/
  1. Đếm địa chỉ cũ còn sót trong HTML trang, phải ra 0:
bash
curl -s https://dia-chi-moi/ | grep -o "dia-chi-cu" | wc -l
  1. Mở một giá trị serialize có chứa URL để chắc nó chưa bị hỏng. Ở site thử là:
bash
wp theme mod get header_image

Những gì mình chưa kiểm

  • Chưa chuyển sang máy chủ thật hay hosting thật, chưa đổi DNS, chưa đo HTTPS.
  • Chuyển hosting WordPress không đổi tên miền và giữ nguyên thư mục thì URL trong cơ sở dữ liệu giữ nguyên; ca đó mình chưa chạy thử.
  • Chưa thử plugin chuyển site nào, chưa đo site có WooCommerce hay page builder.

Trước khi chuyển, nhớ sao lưu đủ cả cơ sở dữ liệu lẫn wp-content theo bài Khôi phục website WordPress: sao lưu đủ hai phần. Có bản sao lưu đủ thì chuyển hỏng vẫn quay lại được.