Reading Time: 6 minutes

Các quy tắc rewrite trong Nginx có tác dụng thay đổi toàn bộ hoặc một phần URL mà client yêu cầu. Mục đích chính là để cho client biết rằng tài nguyên họ đang tìm đã được chuyển đi nơi khác, bên cạnh đó là để kiểm soát luồng thực thi của các trang trong Nginx.

Rewrite URL trong Nginx

Các directive (chỉ dẫn để máy chủ thực thi một hành động) returnrewrite trong Nginx có cùng chức năng là để rewrite URL. Tuy nhiên, directive rewrite mạnh hơn vì nó có thể thực hiện các thao tác rewrite phức tạp đòi hỏi phải phân tích cú pháp (parsing) của URL.

Trong bài hướng dẫn này, chúng ta sẽ tìm hiểu cách sử dụng cả hai directive returnrewrite trong Nginx để thay đổi hoặc rewrite URL.

Directive return trong Nginx

Cách dễ và gọn nhất để rewrite một URL là sử dụng directive return. Nó phải được khai báo trong context (ngữ cảnh chỉ rõ phạm vi tác dụng của các lệnh) server hoặc location bằng cách chỉ định URL cần chuyển hướng đến.

Directive return trong server context

Directive return trong server context rất có ích trong trường hợp bạn đã di chuyển trang web của mình sang một domain mới và muốn chuyển hướng tất cả URL cũ sang domain đó. Ngoài ra, nó cũng giúp chuẩn hóa (canonicalization) URL bằng cách buộc trang web của bạn chuyển hướng đến phiên bản có www hoặc không có www.

server {
        listen 80;
        server_name www.olddomain.com;
        return 301 $scheme://www.newdomain.com$request_uri;
}

Directive return trong server context ở trên sẽ chuyển hướng URL từ www.olddomain.com sang newdomain.com. Ngay khi Nginx nhận được một URL với www.olddomain.com, nó sẽ ngừng xử lý trang và gửi mã phản hồi 301 cùng với URL đã được rewrite cho client.

Hai biến được sử dụng trong directive return ở trên là $scheme$request_uri. Biến $scheme được dùng để xác định scheme của URL (http hoặc https) và biến $request_uri chứa URI hoàn chỉnh cùng với các tham số (nếu có). Lưu ý rằng cả hai biến này đều lấy thông tin từ URL đầu vào khi thực hiện rewrite.

Directive return trong location context

Trong một số trường hợp, bạn có thể muốn chuyển hướng các trang cụ thể thay vì chuyển hướng cả domain. Directive return bên trong khối location cho phép bạn chuyển hướng các trang cụ thể đến một địa chỉ mới.

location = /tutorial/learning-nginx {
     return 301 $scheme://example.com/nginx/understanding-nginx
}

Trong ví dụ trên, bất cứ khi nào một request URI khớp chính xác với mẫu /tutorial/learning-nginx, Nginx sẽ chuyển hướng nó đến địa chỉ mới https://example.com/nginx/understanding-nginx/.

Bạn cũng có thể chuyển hướng mọi thứ thuộc một đường dẫn cụ thể đến một địa chỉ mới. Ví dụ sau đây chuyển hướng tất cả các trang nằm dưới /tutorial/ sang https://example.com/articles.

location /tutorial {
     return 301 $scheme://example.com/articles
}

Directive rewrite trong Nginx

Ta cũng có thể sử dụng directive rewrite để rewrite URL trong Nginx. Giống như return, rewrite cũng có thể được đặt trong server context cũng như trong location context.

Directive này có thể thực hiện các tác vụ phân loại phức tạp giữa các URL và lấy ra các thành phần từ URL gốc mà không có biến Nginx tương ứng. Khả năng này giúp làm cho nó hữu ích hơn directive return.

Cú pháp của directive rewrite là:

rewrite regex replacement-url [flag];
  • regex: Biểu thức chính quy (regular expression) dựa trên PCRE sẽ được sử dụng để so khớp với request URI đến.
  • replacement-url: Nếu biểu thức chính quy khớp với URI được yêu cầu, chuỗi thay thế này sẽ được sử dụng để thay đổi URI đó.
  • flag: Giá trị của flag (cờ tùy chọn) sẽ quyết định xem có cần xử lý thêm các directive rewrite khác hay không.

Lưu ý là directive rewrite chỉ có thể trả về mã 301 hoặc 302. Để trả về các mã khác, bạn cần phải thêm một directive return một cách tường minh sau rewrite.

Ví dụ dùng directive rewrite trong Nginx

Hãy cùng nhanh chóng xem qua một vài cách dùng rewrite đơn giản mà bạn có thể tham khảo, từ việc rewrite một trang html đơn giản đến một URL khác.

Rewrite trang tĩnh

Giả sử bạn muốn rewrite một URL cho một trang, ví dụ https://example.com/nginx-tutorial thành https://example.com/somePage.html. Ta có thể làm điều này với directive rewrite trong khối location như sau:

server {
          ...
          ...
          location = /nginx-tutorial 
          { 
            rewrite ^/nginx-tutorial?$ /somePage.html break; 
          }
          ...
          ...
}

Giải thích:

  • Directive location = /nginx-tutorial cho chúng ta biết rằng khối location sẽ chỉ khớp với một URL chứa chính xác tiền tố là /nginx-tutorial.
  • Nginx sẽ tìm kiếm pattern ^/nginx-tutorial?$ trong URL được yêu cầu.
  • Để định nghĩa pattern, các ký tự ^, ?$ được sử dụng và có ý nghĩa đặc biệt. ^ đại diện cho phần đầu của chuỗi cần khớp. $ đại diện cho phần cuối của chuỗi cần khớp. ? đại diện cho non-greedy modifier (ký tự điều chỉnh sẽ ngừng việc tìm kiếm pattern ngay khi đã tìm thấy một kết quả khớp).
  • Nếu request URI chứa pattern trên, somePage.html sẽ được dùng làm chuỗi thay thế.
  • Vì quy tắc rewrite kết thúc bằng break, việc rewrite cũng sẽ dừng lại, nhưng request đã được rewrite sẽ không được chuyển đến một location khác để xử lý tiếp.

Rewrite trang động

Bây giờ hãy xem xét một trang động https://www.example.com/user.php?id=11 trong đó phần động là id=11(userid). Chúng ta muốn URL được rewrite thành https://exampleshop.com/user/11.

Nếu bạn có 10 người dùng, bạn sẽ cần 10 quy tắc rewrite cho mỗi người nếu bạn theo phương pháp rewrite URL ở trên. Thay vào đó, ta có thể dùng các biến để bắt các thành phần trong URL và dùng chúng để xây dựng một quy tắc rewrite duy nhất sẽ xử lý tất cả các trang động như sau:

server {
          ...
          ...
          location = /user.php 
          { 
            rewrite user.php?id=$1 ^user/([0-9]+)/?$ break; 
          }
          ...
          ...
}

Giải thích:

  • Directive location = /user bảo Nginx tìm khối location khớp với một URL chứa chính xác tiền tố là /user.
  • Nginx sẽ tìm kiếm pattern ^user/([0-9]+)/?$ trong URL được yêu cầu.
  • Biểu thức chính quy trong cặp ngoặc vuông [0-9]+ chứa một dải ký tự từ 0 đến 9. Dấu + biểu thị việc khớp một hoặc nhiều ký tự đứng trước nó. Nếu không có dấu +, biểu thức chính quy trên sẽ chỉ khớp với một ký tự duy nhất như 5 hoặc 8 chứ không phải 25 hay 44.
  • Cặp dấu ngoặc đơn ( ) trong biểu thức chính quy dùng để chỉ back-reference (tham chiếu ngược). Biến $1 trong URL thay thế user.php?id=$1 chính là tham chiếu đến back-reference này.

Ví dụ, nếu http://www.example.com/user/24 là URL đầu vào, thì ID của user (24) sẽ khớp với dải ký tự trong back-reference, dẫn đến kết quả thay thế như sau: https://www.example.com/user.php?id=24

Cách rewrite URL nâng cao

Giả sử ta muốn URL https://www.example.com/user.php?user_name= được rewrite thành https://www.example.com/user/login/john. Không giống như quy tắc trước, phần động của URL user_name=john bây giờ chứa một dải các ký tự chữ cái.

Ta có thể sử dụng quy tắc rewrite như dưới đây:

server {
          ...
          ...
          location = /user.php 
            { 
                rewrite user.php?user_name=$1 ^user/login/([a-z]+)/?$ break;           
            }
          ...
          ...
  }

Giải thích:

  • Directive location = /user/login/john cho Nginx biết rằng khối location sẽ phải khớp với một URL chứa tiền tố chính xác là /user/login/john.
  • Nginx sẽ tìm kiếm pattern ^user/login/([a-z]+)/?$ trong URL được yêu cầu.
  • Biểu thức chính quy trong cặp ngoặc vuông [a-z]+ chứa một dải ký tự từ a đến z. Dấu + có nghĩa là khớp một hoặc nhiều ký tự đứng trước nó. Nếu không có dấu +, biểu thức chính quy trên sẽ chỉ khớp với một ký tự duy nhất như a hoặc c chứ không phải john hay doe.
  • Cặp dấu ngoặc đơn ( ) trong biểu thức chính quy dùng để chỉ back-reference. Biến $1 trong URL thay thế user.php?user_name=$1 chính là tham chiếu đến back-reference này.

Ví dụ, nếu URL đầu vào là https://www.example.com/user/login/john thì tên người dùng “john” sẽ khớp với dải ký tự trong back-reference, tạo ra thay thế như sau: https://www.example.com/user.php?user_name=john

Rewrite với nhiều back-reference

Trong ví dụ này, chúng ta cũng sẽ tìm hiểu cách rewrite một URL bằng cách sử dụng nhiều back-reference. Giả sử URL đầu vào là https://example.com/tutorial/linux/wordpress/file1 và chúng ta muốn rewrite nó thành https://example.com/tutorial/linux/cms/file1.php.

Nếu bạn nhìn kỹ vào URL đầu vào, nó bắt đầu bằng /tutorial, và ở đâu đó trong đường dẫn, chuỗi wordpress cần được thay thế bằng chuỗi cms. Hơn nữa, phần mở rộng file (php) cũng cần được thêm vào cuối tên file.

Quy tắc rewrite cho kịch bản này là:

server {
          ...
          ...
          location /tutorial
          {
             rewrite ^(/tutorial/.*)/wordpress/(\\w+)\\.?.*$ $1/cms/$2.php last;
          }
          ...
          ...
  }

Giải thích:

  • Back-reference đầu tiên ^(/tutorial/.*) trong biểu thức chính quy được dùng để khớp với bất kỳ URL đầu vào nào bắt đầu bằng /tutorial/foo.
  • Back-reference thứ hai (\\w+) được dùng để bắt tên file không có phần mở rộng.
  • Hai back-reference trên được sử dụng trong URL thay thế thông qua $1$2.
  • Từ khóa last bảo Nginx ngừng phân tích các điều kiện rewrite tiếp theo, ngay cả khi có một location khác cũng khớp.

Tổng kết

Bây giờ bạn đã có thể rewrite URL trong Nginx bằng cách vận dụng linh hoạt directive rewrite và return. Hãy chủ động áp dụng các quy tắc này vào hệ thống của bạn để kiểm soát việc điều hướng truy cập hiệu quả hơn.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất