nginx njs: cài mô-đun, thiết lập js_set và áp dụng reload để tạo biến JavaScript

nginx njs là mô-đun chính thức của nginx dùng hàm JavaScript ngay trong cấu hình nginx để xử lý yêu cầu hoặc tạo giá trị biến. Trên máy chủ Nginx + Apache, để khớp chữ hoa chữ thường của URL yêu cầu, điều mà riêng cấu hình nginx không thay đổi được, mô-đun njs đã được cài cùng phiên bản với nginx đang cài, rồi biến tạo bằng js_set được đưa vào đường dẫn tệp cache và áp dụng bằng reload mà không cần khởi động lại.
Hàm ví dụ chỉ là một dòng đổi mã hóa phần trăm của URL tiếng Hàn sang chữ thường, và cách đọc giá trị header hay đối số từ đối tượng yêu cầu để tạo biến cũng tương tự.
1️⃣ nginx njs là gì: mô-đun mở rộng chức năng nginx bằng JavaScript
njs không phải môi trường chạy JavaScript độc lập như Node.js: nó không có script khởi động hay vòng lặp sự kiện riêng, và mã chỉ chạy khi nginx gọi hàm được chỉ định trong cấu hình, sau đó việc xử lý quay về nginx.
Các chỗ nginx gọi hàm JavaScript đã được định sẵn: giai đoạn xử lý yêu cầu và phản hồi HTTP, giai đoạn xử lý phiên TCP·UDP, tính giá trị biến và bộ hẹn giờ định kỳ. Hàm đọc URL, header và đối số từ đối tượng yêu cầu mà nginx truyền vào, rồi trả kết quả về dưới dạng phản hồi hoặc giá trị biến.
Ở phía HTTP, [js_import] nạp tệp script, [js_set] tạo giá trị biến, [js_content] tạo nội dung phản hồi, còn [js_header_filter] và [js_body_filter] chỉnh header và nội dung phản hồi. Lần áp dụng này dùng hai chỉ thị: [js_import] và [js_set].
Engine mặc định của mô-đun njs triển khai ECMAScript 5.1 (strict mode) cộng thêm một số tính năng từ ECMAScript 6 trở đi. Xử lý chuỗi ở mức thay thế bằng regex và hàm chạy được trong phạm vi này.
2️⃣ Khi nào cần njs: giá trị biến mà riêng cấu hình nginx không đổi được
Plugin WordPress WP Rocket đặt tên thư mục cache của bài viết có URL tiếng Hàn bằng mã hóa phần trăm chữ thường, còn trình duyệt gửi yêu cầu cùng URL đó mã hóa bằng chữ hoa. Một trang WordPress đang chạy trên máy chủ này.
Nếu nginx tạo đường dẫn tệp cache theo nguyên URL yêu cầu, chữ hoa chữ thường không khớp nên không tìm thấy tệp, và yêu cầu bị chuyển sang Apache và PHP.
Riêng cấu hình nginx không thể đổi giá trị này sang chữ thường. [map] chỉ chọn một trong các giá trị đã đặt chứ không chuyển đổi được chuỗi, và trước khi cài, cả kết quả [nginx -V] lẫn thư mục mô-đun đều không có mô-đun perl hay lua.
Nếu chỉ có vài URL cần đổi thì bảng đối chiếu [map] cũng xử lý được, nhưng phải dựng lại bảng và reload mỗi khi có bài mới. Khi mọi URL đều cần đổi giá trị theo cùng một quy tắc, chỉ một hàm [js_set] là xử lý được cả URL của bài mới, thẻ và chuyên mục.

[perl_set] của mô-đun perl cũng dùng được ở cùng chỗ, và kho có gói đi đôi với nginx, nhưng tài liệu chính thức của nginx coi mô-đun perl là mô-đun thử nghiệm.
3️⃣ Cài nginx njs: cùng phiên bản với nginx đang cài
Cài chỉ bằng tên gói mô-đun trông như chỉ thêm njs, nhưng njs mới nhất trong kho yêu cầu nginx mới nhất nên nginx cũng bị nâng cấp theo. Ở bước kiểm tra trước khi cài, lệnh cài không ghi phiên bản đã cho thấy kế hoạch nâng nginx từ 1.28.0 lên 1.30.6.
Vì vậy hãy kiểm tra phiên bản nginx đang cài trước.
rpm -q nginxnginx-1.28.0-1.el9.ngx.x86_64Kho có gói njs riêng đi đôi với từng phiên bản nginx; trong số phiên bản, phần trước dấu + là phiên bản nginx và phần sau là phiên bản njs.
dnf list --showduplicates nginx-module-njs | grep '1.28.0+'nginx-module-njs.x86_64 1:1.28.0+0.9.4-1.el9.ngx @nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.8.10-1.el9.ngx nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.9.0-1.el9.ngx nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.9.1-1.el9.ngx nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.9.2-1.el9.ngx nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.9.3-1.el9.ngx nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.9.4-1.el9.ngx nginx-stableCó sáu phiên bản đi đôi với nginx 1.28.0, từ 0.8.10 đến 0.9.4, và dòng @nginx-stable trên cùng là phiên bản đã cài xong. Việc cài được thực hiện với phiên bản mới nhất trong số đó là 0.9.4, ghi đầy đủ số phiên bản.
dnf install nginx-module-njs-1:1.28.0+0.9.4-1.el9.ngxCó thể kiểm tra bằng rpm xem gói đã cài yêu cầu phiên bản nginx nào.
rpm -q --requires nginx-module-njs | grep nginxnginx-r1.28.0rpm -ql nginx-module-njs | grep -v build-id/usr/bin/njs
/usr/lib64/nginx/modules/ngx_http_js_module-debug.so
/usr/lib64/nginx/modules/ngx_http_js_module.so
/usr/lib64/nginx/modules/ngx_stream_js_module-debug.so
/usr/lib64/nginx/modules/ngx_stream_js_module.so
/usr/share/doc/nginx-module-njs
/usr/share/doc/nginx-module-njs/CHANGES
/usr/share/doc/nginx-module-njs/COPYRIGHTCác tệp được cài gồm mô-đun HTTP, mô-đun stream xử lý TCP·UDP, phiên bản debug của từng mô-đun và công cụ dòng lệnh njs. Thiết lập lần này chỉ nạp một tệp là mô-đun HTTP [ngx_http_js_module.so].
4️⃣ Thiết lập js_set: từ nạp mô-đun đến đường dẫn tệp cache
Cài gói xong nginx cũng không tự nạp mô-đun; nginx.conf cần có dòng [load_module] ở trên cùng. Theo tài liệu chính thức, [load_module] là chỉ thị ngữ cảnh main nên đặt bên ngoài khối events·http.
# Trên cùng /etc/nginx/nginx.conf (ngoài khối events·http)
load_module modules/ngx_http_js_module.so;Hàm chuyển đổi được đặt trong tệp .js riêng, tách khỏi tệp cấu hình, và cấu hình nginx nạp hàm được xuất bằng export default ở dòng cuối.
// /etc/nginx/njs/rocket.js — chỉ đổi mã hóa phần trăm %XX sang chữ thường
function lc(r) {
return r.variables.request_uri.replace(/%[0-9A-F]{2}/g, function (m) { return m.toLowerCase(); });
}
export default { lc };Hàm đọc biến [request_uri] từ đối tượng yêu cầu r, chỉ đổi sang chữ thường hai ký tự sau dấu phần trăm khi chúng là chữ hoa, rồi trả về. Chữ Latin được giữ nguyên nên URL tiếng Anh có lẫn chữ hoa không thay đổi, và quy tắc này giống cách WP Rocket đặt tên thư mục cache.
# Trong khối http (tệp .conf trong conf.d)
js_import rkt from /etc/nginx/njs/rocket.js;
js_set $rocket_uri rkt.lc;rkt trong [js_import] là tên dùng để gọi hàm trong mô-đun, còn [js_set] biến kết quả của hàm đó thành biến [$rocket_uri]. Theo tài liệu chính thức, hàm này chạy khi biến được tham chiếu lần đầu trong yêu cầu và chỉ hỗ trợ xử lý đồng bộ.
# Khối server — đổi $request_uri trong đường dẫn tệp cache thành $rocket_uri
set $rocket_file '/wp-content/cache/wp-rocket/example.com${rocket_uri}index$rocket_mobile-https.html';Chỗ thay đổi chỉ là một dòng tạo đường dẫn tệp cache này; các điều kiện loại cookie đăng nhập, truy vấn và yêu cầu POST khỏi cache vẫn giữ nguyên.
5️⃣ Áp dụng và kiểm tra: nhật ký lỗi và header phản hồi sau reload
Chạy kiểm tra cấu hình trước, nếu đạt thì reload.
nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successfulsystemctl reload nginxDòng [load_module] mới thêm có hiệu lực bằng reload mà không cần khởi động lại. Nhật ký lỗi ghi một dòng khởi tạo máy ảo njs, và không có lỗi JavaScript nào.
grep 'js vm init' /var/log/nginx/error.log | head -12026/10/17 18:21:35 [notice] 27992#27992: js vm init njs: 0000559FDF6D0E00njs ở cuối dòng là tên engine đang dùng. Dòng này cũng được ghi khi kiểm tra cấu hình bằng [nginx -t], nên hãy đối chiếu với thời điểm reload.
curl -sI 'https://example.com/-/' | grep -i 'x-rocket\|content-type'content-type: text/html
x-rocket-nginx-serving-file: HITNgay cả với URL tiếng Hàn được mã hóa bằng chữ hoa, header cho biết nginx đã trả tệp cache vẫn xuất hiện. Yêu cầu có truy vấn hoặc cookie đăng nhập và yêu cầu POST, sau khi áp dụng, vẫn được chuyển sang PHP mà không có header đó.
6️⃣ Hoàn tác njs và cập nhật nginx
Khi hoàn tác, đổi [$rocket_uri] trong đường dẫn tệp cache về [$request_uri], bỏ tệp cấu hình [js_import]·[js_set] và dòng [load_module], rồi kiểm tra cấu hình và reload.
# Khối server — về lại biến ban đầu
set $rocket_file '/wp-content/cache/wp-rocket/example.com${request_uri}index$rocket_mobile-https.html';Có thể giữ lại gói: không có dòng [load_module] thì nginx không nạp nó, nên chỉ gỡ gói khi muốn xóa hẳn.
dnf remove nginx-module-njsKhi cập nhật nginx, gói njs cũng phải được nâng lên phiên bản đi đôi với nginx mới. Trước khi cập nhật, hãy kiểm tra bằng [dnf list --showduplicates] xem kho đã có njs đi đôi với phiên bản nginx mới chưa.
🔢 FAQ & nội dung đề xuất














ℹ️ Thông báo tiếp thị liên kết
Nội dung trên trang này có chứa liên kết affiliate (tiếp thị liên kết). Khi khách truy cập mua sản phẩm hoặc dịch vụ qua các liên kết này, trang sẽ nhận hoa hồng từ bên bán. Trong quá trình đó, số tiền người mua phải trả (sẽ giảm khi có chương trình khuyến mãi ↓) không hề tăng lên. Giá, khuyến mãi và tình trạng hàng được đăng theo thời điểm viết bài và có thể khác với thực tế, vì vậy hãy kiểm tra lại lần cuối với bên bán trước khi mua. Việc chọn và đánh giá sản phẩm dựa trên tiêu chí riêng của trang; việc có nhận hoa hồng hay không không ảnh hưởng đến thứ tự giới thiệu hay nội dung đánh giá.

