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.

Các directive (chỉ dẫn để máy chủ thực thi một hành động) return và rewrite 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 return và rewrite 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 và $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
rewritekhá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-tutorialcho chúng ta biết rằng khốilocationsẽ 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ự
^,?và$đượ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.htmlsẽ đượ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ộtlocationkhá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 = /userbảo Nginx tìm khốilocationkhớ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ư5hoặc8chứ không phải25hay44. - 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$1trong URL thay thếuser.php?id=$1chí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/johncho Nginx biết rằng khốilocationsẽ 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ưahoặccchứ không phảijohnhaydoe. - Cặp dấu ngoặc đơn
( )trong biểu thức chính quy dùng để chỉ back-reference. Biến$1trong URL thay thếuser.php?user_name=$1chí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
$1và$2. - Từ khóa
lastbảo Nginx ngừng phân tích các điều kiện rewrite tiếp theo, ngay cả khi có mộtlocationkhá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.