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
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ốilocation
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ự
^
,?
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.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ộtlocation
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ốilocation
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ặc8
chứ không phải25
hay44
. - 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ốilocation
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ặcc
chứ không phảijohn
haydoe
. - 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
và$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ộtlocation
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.