Bài 1
nginx chọn khối location thế nào
Một request tới, nhiều khối location cùng khớp. nginx không chọn khối viết đầu tiên,
cũng không chọn khối cụ thể nhất. Nó chạy năm bước cố định, và thứ tự của năm bước đó quyết định
khối nào thắng.
Năm bước, đúng thứ tự
- Có khối
= /đường-dẫnkhớp chính xác không? Có thì thắng ngay, dừng luôn. - Tìm khối tiền tố khớp dài nhất — tính cả khối mang
^~. Ghi nhớ lại, chưa dùng vội. - Khối dài nhất vừa tìm có mang
^~không? Có thì dùng nó và bỏ qua toàn bộ regex. - Thử các regex theo thứ tự viết trong file. Cái đầu tiên khớp thì thắng.
- Không regex nào khớp thì quay lại dùng khối tiền tố đã ghi nhớ ở bước 2.
Bước 4 xét regex theo thứ tự viết, không theo độ cụ thể
Một regex hẹp hơn nhưng viết sau một regex rộng hơn sẽ không bao giờ chạy. Cấu hình đo bên dưới có
đúng trường hợp đó: khối 4 không thắng ở bất kỳ đường dẫn nào trong 14 đường dẫn đã thử, và nginx
vẫn khởi động bình thường, không phát ra cảnh báo nào.
Đo trên nginx thật
Cấu hình dưới đây nạp vào container nginx:1.27-alpine, mỗi khối trả về tên của chính nó,
rồi gọi curl lần lượt 14 đường dẫn.
location = /exact { return 200 "1 exact"; }
location ^~ /images/ { return 200 "2 prefix-uu-tien"; }
location ~ \.(gif|jpg)$ { return 200 "3 regex-gif-jpg"; }
location ~ ^/img/.*\.gif$ { return 200 "4 regex-img-gif"; }
location ~* \.PNG$ { return 200 "5 regex-png-ci"; }
location /img/ { return 200 "6 prefix-img"; }
location /doc { return 200 "7 prefix-doc"; }
location /document { return 200 "8 prefix-document"; }
location / { return 200 "9 mac-dinh"; }
| Đường dẫn | Khối thắng | Vì sao |
|---|---|---|
/exact | 1 exact | khớp chính xác, bước 1 |
/exact/ | 9 mac-dinh | khớp chính xác là khớp từng ký tự; thêm một dấu / là URI khác |
/images/a.gif | 2 prefix-uu-tien | ^~ chặn regex ở bước 3, dù khối 3 cũng khớp |
/images/b.txt | 2 prefix-uu-tien | cùng khối, ở đây không regex nào khớp nên kết quả trùng nhau |
/img/a.gif | 3 regex-gif-jpg | regex viết trước thắng; khối 4 cụ thể hơn nhưng không bao giờ chạy |
/img/a.GIF | 6 prefix-img | cả khối 3 và 4 đều dùng ~ phân biệt hoa thường, không khớp .GIF, nên rơi về bước 5 |
/img/b.txt | 6 prefix-img | không regex nào khớp nên quay lại tiền tố, bước 5 |
/photo.gif | 3 regex-gif-jpg | regex khớp |
/photo.PNG | 5 regex-png-ci | ~* không phân biệt hoa thường |
/photo.png | 5 regex-png-ci | khối 3 chỉ có gif và jpg |
/doc | 7 prefix-doc | tiền tố khớp |
/document | 8 prefix-document | tiền tố dài hơn thắng |
/documentation | 8 prefix-document | vẫn là tiền tố dài nhất |
/khac | 9 mac-dinh | chỉ còn / |
Một lỗi gặp ngay khi dựng bộ đo này
Bản cấu hình đầu tiên có cả
location ^~ /images/ lẫn location /images/.
nginx từ chối khởi động:
[emerg] duplicate location "/images/".
Modifier không tạo ra khối riêng — ^~ /x/ và /x/ là cùng một
location. Phòng thí nghiệm bên dưới tái hiện đúng lỗi này nếu bạn viết cả hai.
Phòng thí nghiệm
Sửa cấu hình và đường dẫn tuỳ ý. Thuật toán chạy ngay trên trình duyệt là bản viết lại năm bước ở trên, và nó đã được đối chiếu với nginx 1.27.5 thật trên đúng 14 đường dẫn trong bảng — khớp 14/14.
Các khối location
Đường dẫn cần thử
nginx xét theo thứ tự này
Ba chỗ hay hỏng
-
Regex hẹp viết sau regex rộng. Trong bảng trên, khối 4
(
^/img/.*\.gif$) không bao giờ chạy vì khối 3 (\.(gif|jpg)$) khớp trước. Muốn khối 4 có tác dụng thì phải viết nó lên trên. -
Dùng
=cho đường dẫn có thể có dấu/ở cuối.= /exactkhông bắt/exact/. Nếu cần cả hai thì viết hai khối, hoặc dùng tiền tố. -
Tưởng
^~là "ưu tiên cao". Nó chỉ có nghĩa "nếu tiền tố này là tiền tố dài nhất thì đừng thử regex nữa". Một tiền tố khác dài hơn vẫn thắng nó.
Tự kiểm tra
Có location /api/ và location ~ \.json$. Request /api/data.json vào khối nào?
Vào khối regex. Tiền tố /api/ được ghi nhớ ở bước 2 nhưng không mang ^~, nên bước 4 vẫn chạy và regex khớp. Muốn tiền tố thắng thì đổi thành location ^~ /api/.
Vì sao /img/b.txt vào khối tiền tố còn /img/a.gif vào khối regex?
Cả hai đều có tiền tố dài nhất là /img/, nhưng bước 4 chạy trước bước 5. Với .txt không regex nào khớp nên mới quay lại tiền tố; với .gif thì regex khớp và thắng luôn.
Cùng cấu hình đó, /img/a.GIF vào khối nào?
Khối 6, tiền tố /img/. Khối 3 và khối 4 đều dùng ~ nên phân biệt hoa thường và không khớp phần mở rộng viết hoa; chỉ khối 5 dùng ~*, nhưng nó tìm .PNG. Đây là số đo thật, không phải suy luận.
Thêm location ^~ /img/ vào cấu hình trong bảng thì chuyện gì xảy ra?
nginx không khởi động: đã có location /img/, và modifier không tạo khối riêng nên đây là trùng location. Thử ngay trong phòng thí nghiệm phía trên.