Dùng NGINX làm Reverse Proxy cho các dịch vụ web giúp hiệu quả trong việc cải thiện hiệu năng, sự ổn định và bảo mật của dịch vụ.
Vì sao nên sử dụng Reverse Proxy
Reverse Proxy hỗ trợ cân bằng tải (Load Balancing) với khả năng phân phối request từ người dùng tới không chỉ một mà là một nhóm các máy chủ dịch vụ, để đảm bảo không máy chủ nào bị quá tải hoặc dịch vụ bị gián đoạn do máy chủ nào đó không hoạt động. Từ đó đảm bảo sự ổn định của dịch vụ.
Reverse Proxy như một “chốt chặn” đứng giữa các dịch vụ web và người dùng, có khả năng “thanh lọc” các request từ người dùng đến server. Vì vậy phần nào bảo vệ dịch vụ web khỏi các cuộc tấn công.
Reverse Proxy giúp tăng tốc cho các dịch vụ web nếu hỗ trợ các tính năng như Caching, TLS Acceleration (Mã hóa TLS/SSL bằng phần cứng), hoặc Intelligent Compression (Loại bỏ dữ liệu thừa (minify) để giảm kích thước dữ liệu).
Ngoài ra, Reverse Proxy còn giúp các dịch vụ chạy trên các nền tảng khác nhau (ví dụ Apache, NodeJS, Ruby on Rails v.v…), hoặc đang listen ở những cổng khác nhau về chung port trên cùng tên miền, từ đó giúp chúng dễ dàng liên hệ với nhau mà không lo CORS.
Reverse Proxy có thể thực hiện với phần mềm như Squid, Apache HTTPD (mod_proxy), NGINX hay HAProxy v.v…
Dùng NGINX làm reverse proxy
Với việc cài đặt và cấu hình đơn giản, thiết kế hiệu quả trong xử lý lượng request lớn, và hỗ trợ mọi tính năng ‘proxy’ mạnh mẽ, NGINX là lựa chọn phổ biến để làm reverse proxy. Tôi hay dùng NGINX làm reverse proxy cho các ứng dụng Node.js.
Do yêu cầu của một số project, tôi thường cấu hình mỗi app sẽ nằm ở một subfolder thay vì mỗi app một domain để hạn chế CORS. File cấu hình cơ bản như sau:
server { listen 80; server_name example.com *.example.com; # Allow asset caching location ^~ /assets/ { gzip_static on; expires 12h; add_header Cache-Control public; } location / { proxy_pass http://127.0.0.1:8000; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; proxy_redirect off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; } location /endpoint1/ { rewrite ^/endpoint1/(.*)$ /$1 break; proxy_pass http://127.0.0.1:8001; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; proxy_redirect off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; } #### Other endpoints ##### access_log /var/log/nginx/reverse-access.log; error_log /var/log/nginx/reverse-error.log; }
Với cấu hình trên, đường dẫn root (https://example.com/) tôi sử dụng cho app đang listen tại http://127.0.0.1:8000 và đường dẫn /endpoint1 (https://example.com/endpoint1/) tôi hướng đến một app khác đang listen tại http://127.0.0.1:8001.
Một số thông tin về cấu hình:
proxy_http_version 1.1
– Phiên bản HTTP dùng cho proxy (mặc định là 1.0, ở đây ta đặt là 1.1).proxy_cache_bypass $http_upgrade
– Ngăn lấy dữ liệu từ cache khi dịch vụ có sử dụng Websocket.Upgrade $http_upgrade
vàConnection "upgrade"
– Bắt buộc khi dịch vụ có sử dụng Websocket.X-Real-IP $remote_addr
– Forward địa chỉ IP của client đến server đằng sau proxy.X-Forwarded-For $proxy_add_x_forwarded_for
– Danh sách địa chỉ IP của các server mà client được ‘proxy’ đến.X-Forwarded-Proto $scheme
– Dùng khi proxy hỗ trợ HTTPS. Mỗi HTTP response từ server phía sau proxy sẽ được proxy server chuyển thành HTTPS.X-Forwarded-Host $host
– Địa chỉ Host được nhận request từ phía client .X-Forwarded-Port $server_port
– Port của server nhận request từ phía client.
Lưu ý: Với các endpoint là subfolder như ví dụ trên là /endpoint1
, tôi phảibổ sung một setting rewrite tương ứng là rewrite ^/endpoint1/(.*)$ /$1 break;
để fix lỗi các đường dẫn sau /endpoint1 (ví dụ /endpoint1/abc) luôn bị trỏ đến /endpoint1.
Dùng NGINX làm load balancer
(Còn tiếp)
Bảo mật NGINX reverse proxy với SSL của Let’s Encrypt
Trước tiên ta cần cài đặt certbot và tiến hành xác thực bằng DNS do trường hợp này ta không có www root hoặc dùng wildcard domain.
certbot certonly \ --manual \ --preferred-challenges dns \ -d example.com \ -d *.example.com
Sửa lại file cấu hình nginx trên để thêm cấu hình SSL
server { listen 80; server_name example.com *.example.com; # Allow asset caching location ^~ /assets/ { gzip_static on; expires 12h; add_header Cache-Control public; } location / { proxy_pass http://127.0.0.1:8000; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; proxy_redirect off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; } location /endpoint1/ { rewrite ^/endpoint1/(.*)$ /$1 break; proxy_pass http://127.0.0.1:8001; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; proxy_redirect off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; } #### Other endpoints ##### #### Let's Encrypt certificate #### listen 443 ssl; # RSA certificate ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; # Redirect non-https traffic to https if ($scheme != "https") { return 301 https://$host$request_uri; } access_log /var/log/nginx/reverse-access.log; error_log /var/log/nginx/reverse-error.log; }