Chuyển website từ Apache sang Nginx mà không mất URL SEO

Quy trình chuyển Apache sang Nginx an toàn: inventory, chuyển VirtualHost, đổi .htaccess, bảo toàn URL, kiểm thử redirect và chuẩn bị rollback.

NHT
· 9 phút đọc
Minh họa đường chuyển website từ Apache sang Nginx với bản đồ URL SEO và cổng kiểm thử

Chuyển một website từ Apache sang Nginx không chỉ là đổi từ VirtualHost sang server. Phần dễ gây thiệt hại nhất là các URL cũ, quy tắc rewrite trong .htaccess, trailing slash, query string, file tĩnh, endpoint API và canonical URL. Nếu migration làm thay đổi đường dẫn mà không có mapping, Google và người dùng sẽ gặp 404 hoặc bị chuyển hướng sai.

Cách an toàn là coi đây như một dự án migration có inventory, mapping, môi trường kiểm thử, cutover và rollback. Nginx có module rewrite để đổi URI bằng biểu thức PCRE và trả redirect; Nginx cũng có proxy module để chuyển request sang upstream [1] [2]. Những khả năng này đủ cho nhiều kiến trúc, nhưng cú pháp và thứ tự xử lý không phải bản sao của Apache mod_rewrite.

Nguyên tắc cốt lõi: URL cũ phải được coi là dữ liệu cần bảo toàn. Trước khi đổi server, hãy lập danh sách URL có traffic hoặc backlink, tạo mapping một-một sang URL mới, trả 301 khi đường dẫn thực sự thay đổi và xác minh Location, canonical, sitemap cùng status code sau cutover.

Apache và Nginx khác nhau ở mô hình nào?

Apache thường đọc cấu hình theo VirtualHost và có thể xử lý .htaccess ở từng thư mục. Nginx tập trung cấu hình vào file chính, các server block và location; nó không tự đọc .htaccess. Vì vậy, copy file .htaccess vào document root sau migration sẽ không làm các redirect tự hoạt động.

Một VirtualHost Apache có thể chứa nhiều phần: domain, SSL, document root, Directory permission, PHP handler, proxy, cache header và mod_rewrite. Trước khi viết Nginx config, hãy tách từng trách nhiệm thành bảng inventory. Nếu website dùng PHP-FPM, hãy xác định socket hoặc port; nếu dùng Node/Laravel/WordPress qua upstream, hãy xác định process và header cần chuyển.

Bước 1: lập inventory trước khi đụng DNS

Đừng bắt đầu bằng việc tắt Apache. Hãy thu thập cấu hình Apache đang chạy, danh sách domain/subdomain, document root, certificate, cron, upload directory, file log, PHP version, database endpoint và các service phụ trợ. Từ access log, Search Console, sitemap, analytics và backlink report, tạo danh sách URL cần kiểm tra.

  • Domain chính, www/non-www, HTTP/HTTPS và các subdomain có tồn tại.
  • URL bài viết, trang, category/tag, feed, file download, ảnh và endpoint API.
  • Các URL đã có click, impression, backlink hoặc từng được index.
  • Quy tắc rewrite, redirect, canonical, robots.txt và sitemap hiện tại.
  • Cookie path/domain, upload size, timeout, header bảo mật và cache header.
  • Phiên bản PHP, socket PHP-FPM hoặc upstream app nếu Nginx không chạy PHP trực tiếp.
# Ví dụ kiểm kê URL từ sitemap và một số URL quan trọng
curl -fsSL https://example.com/sitemap.xml -o sitemap-before.xml
curl -sSI https://example.com/
curl -sSI https://www.example.com/
curl -sSI http://example.com/old-path

# Tìm rule rewrite trong cấu hình Apache (cần quyền phù hợp)
grep -RInE "RewriteRule|RewriteCond|Redirect|ServerName|DocumentRoot" \
  /etc/apache2 /etc/httpd 2>/dev/null | head -200

Nếu không có quyền đọc log hoặc cấu hình trên shared hosting, hãy xin bản export từ nhà cung cấp. Không nên đoán mapping từ vài URL mẫu, vì các website lâu năm thường có nhiều biến thể slug, thư mục cũ và file tải xuống.

Bước 2: chuyển VirtualHost thành server block

Server block Nginx cần thể hiện rõ domain, root, index, log, SSL và location. Với website tĩnh, try_files giúp trả file tồn tại hoặc fallback về entry point của SPA. Với PHP, request thường được chuyển qua PHP-FPM; với ứng dụng Node hoặc container, dùng proxy_pass và chuyển header Host, IP thật nếu upstream cần.

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example/current/public;
    index index.html index.php;

    access_log /var/log/nginx/example.access.log;
    error_log  /var/log/nginx/example.error.log;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    location ~* \.(?:css|js|jpg|jpeg|png|gif|svg|webp|ico|woff2)$ {
        expires 7d;
        add_header Cache-Control "public, max-age=604800";
        try_files $uri =404;
    }
}

Tên socket PHP-FPM chỉ là ví dụ; không được copy nguyên xi nếu máy bạn dùng phiên bản khác. Bài hướng dẫn nhiều phiên bản PHP và PHP-FPM của TechNT có thể dùng để đối chiếu cách xác định upstream phù hợp. Sau khi viết config, kiểm tra quyền đọc document root và quyền socket trước khi kết luận Nginx bị lỗi.

Bước 3: chuyển .htaccess và RewriteRule có kiểm soát

Module ngx_http_rewrite_module xử lý các directive break, if, return, rewriteset. Nginx thực thi directive ở server level, tìm location rồi xử lý tiếp; một URI bị rewrite không nên tạo vòng lặp vô hạn. Tài liệu chính thức nêu giới hạn tối đa 10 chu kỳ rewrite trước khi trả lỗi [1].

Với redirect SEO, ưu tiên mapping cụ thể và return 301 khi có thể. Dùng regex tổng quát chỉ khi bạn đã kiểm thử toàn bộ nhóm URL. Cẩn thận với query string: theo tài liệu, các argument cũ có thể được giữ lại tùy replacement; thêm dấu hỏi ở cuối replacement khi bạn thực sự muốn bỏ query string [1].

# Apache cũ: ví dụ minh họa
RewriteEngine On
RewriteRule ^old-post/?$ /new-post/ [R=301,L]
RewriteRule ^category/(.*)$ /topics/$1 [R=301,L]
# Nginx mới: ưu tiên rule cụ thể
server {
    server_name example.com;

    location = /old-post {
        return 301 /new-post/;
    }

    location ~ ^/category/(.+)$ {
        return 301 /topics/$1;
    }
}

Không nên nhồi hàng trăm rule vào một regex khó đọc nếu có thể tạo file mapping riêng hoặc chia theo nhóm. Với migration lớn, hãy lưu mapping trong Git, review như code và tạo test table gồm URL đầu vào, status mong đợi, Location mong đợi và query string mong đợi.

Bước 4: giữ nguyên URL bằng redirect 301

Nếu URL không đổi, hãy để request trả 200 tại đúng đường dẫn cũ. Nếu URL bắt buộc đổi, trả một redirect 301 server-side đến URL mới gần nhất. Tránh chuỗi /old/temporary/new; hãy đi thẳng từ URL cũ đến canonical mới. Không redirect mọi 404 về homepage vì điều đó tạo soft 404 và làm mất ngữ nghĩa.

# HTTP chuyển thẳng sang HTTPS, giữ host và URI
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

# Một URL cũ chuyển một lần sang slug mới
location = /san-pham-cu {
    return 301 https://example.com/san-pham-moi/;
}

Hãy quyết định rõ canonical host trước cutover: www hay non-www, HTTP hay HTTPS, trailing slash hay không. Redirect ở lớp host nên không che mất redirect nội dung. Sau khi chạy, dùng curl để xác định số lần chuyển hướng và giá trị Location cuối cùng.

Bước 5: nếu chưa thể bỏ Apache, dùng Nginx làm reverse proxy

Migration không nhất thiết phải là “tắt Apache rồi bật Nginx” trong một lần. Nginx có thể đứng trước và proxy request đến Apache trong giai đoạn chuyển tiếp. Tài liệu NGINX mô tả reverse proxy là cách chuyển request đến server khác, đồng thời có thể sửa request header và tinh chỉnh buffering [3]. Module proxy chính thức dùng proxy_pass và các directive header để chuyển tiếp request [2].

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
        proxy_read_timeout 60s;
    }
}

Mô hình này cho phép bạn kiểm tra Nginx, TLS, header và redirect trong khi Apache vẫn phục vụ ứng dụng. Tuy nhiên, cần cấu hình trusted proxy trong framework để URL sinh ra dùng đúng scheme HTTPS, nếu không có thể xuất hiện redirect loop hoặc asset URL trỏ về HTTP.

Bước 6: chạy song song và cutover có rollback

Dựng server block mới trên IP hoặc port kiểm thử, dùng file hosts cục bộ hoặc hostname tạm để kiểm tra trước khi đổi DNS. Nếu có CDN, hãy giảm TTL trước thời điểm cutover theo chính sách nhà cung cấp. Kiểm tra nhóm URL đại diện: homepage, bài cũ, bài mới, category, tag, search, file download, form, API, ảnh và trang 404.

# Kiểm tra cấu hình trước khi reload
sudo nginx -t
sudo systemctl reload nginx

# Theo dõi redirect và status
curl -sSIL --max-redirs 10 https://example.com/old-post/
curl -sSI https://example.com/new-post/
curl -sSI https://example.com/robots.txt
curl -sSI https://example.com/sitemap.xml

# Nếu có host kiểm thử, ép DNS bằng --resolve
curl -sSIL --resolve example.com:443:203.0.113.10 \
  https://example.com/old-post/

Rollback phải là một thao tác đã được viết trước: khôi phục server block cũ, reload cấu hình, đưa DNS hoặc upstream về trạng thái trước, sau đó kiểm tra một URL đại diện. Không xóa Apache ngay sau khi thấy homepage hoạt động; hãy giữ fallback đủ lâu để xử lý các URL hiếm gặp và tác vụ cron.

Checklist SEO sau khi chuyển server

  • So sánh sitemap trước và sau; URL quan trọng phải giữ nguyên nếu không có lý do đổi.
  • Thu thập danh sách redirect 301 và kiểm tra không có chain, loop hoặc redirect nhầm homepage.
  • Kiểm tra status 200 của URL mới, status 404 thực sự của URL không tồn tại và header canonical.
  • Kiểm tra robots.txt, sitemap.xml, feed, hreflang nếu có và các file tĩnh được tham chiếu trong HTML.
  • Theo dõi Search Console: Page indexing, redirect, soft 404, server error và crawl stats sau cutover.
  • So sánh log 404 và top landing pages trước/sau để bổ sung mapping còn thiếu.
  • Xác minh cookie, form đăng nhập, upload, webhook và API không bị mất header hoặc sai scheme.
  • Giữ bản backup Apache config, Nginx config, mapping URL và database trước khi xóa hệ thống cũ.

Các lỗi thường gặp khi migration Apache sang Nginx

Lỗi đầu tiên là nghĩ rằng Nginx đọc .htaccess. Lỗi thứ hai là dùng try_files sai khiến mọi URL trả homepage hoặc 404. Lỗi thứ ba là proxy thiếu HostX-Forwarded-Proto, làm ứng dụng sinh canonical hoặc redirect sai. Lỗi thứ tư là sửa regex nhưng không kiểm tra query string, chữ hoa/chữ thường và dấu slash.

Một lỗi khác là chỉ kiểm tra homepage. Homepage 200 không chứng minh bài viết, tag, ảnh, API hay trang thanh toán hoạt động. Migration cần được đánh giá theo tập URL có trọng số, ưu tiên những URL có traffic, backlink và doanh thu. Khi có lỗi, dùng access log, error log và curl để tách vấn đề DNS, TLS, redirect, permission, upstream hay ứng dụng.

Kết luận

Chuyển Apache sang Nginx an toàn là bài toán bảo toàn hành vi, không chỉ thay đổi web server. Hãy inventory trước, chuyển từng nhóm cấu hình, tạo mapping URL cụ thể, dùng 301 đúng chỗ, kiểm thử bằng host tạm và chuẩn bị rollback. Nếu cần giảm rủi ro, đặt Nginx làm reverse proxy trước Apache trong giai đoạn đầu rồi mới chuyển từng workload.

Bạn có thể đọc thêm bài cấu hình Nginx reverse proxy cho nhiều website và bài chạy nhiều phiên bản PHP với Nginx và PHP-FPM để hoàn thiện phần upstream sau migration.

Nguồn tham khảo

[1] NGINX Rewrite Module; [2] NGINX Proxy Module; [3] NGINX Reverse Proxy Documentation; [4] Creating NGINX Rewrite Rules.