:::

NGINX作為反向代理伺服器的規劃 / Building a Reverse Proxy with NGINX

6月 23, 2023 , , 0 Comments Edit Copy Download

2023-0409-234228.png

我希望NGNIX不僅是反向代理伺服器,也能夠成為保護其他伺服器的防火牆,還是提高網頁服務效率的加速器。


建構規劃 / Planning

Reverse_proxy_h2g2bob.svg.png

(圖片來源:Wikipedia)

反向代理伺服器可說是NGINX最主要的功能之一。但要建構一個好的反向代理伺服器,可不只單純的從後端拉資料的「proxy_pass」這麼簡單。這邊我只打算簡單地整理一下到目前為止的構思,並附上相關的參考資源。主要是拓展大家對於NGINX可以負責工作的想象。

大致上整體規劃包含了五個層面:

  1. 反向代理
  2. 負載平衡
  3. 隱藏伺服器資訊
  4. 流量限制
  5. 提高效率

接下來就讓我們一個一個來看。


反向代理 / Reverse proxy

2023-0409-213026.png

https://www.programonaut.com/setup-ssl-with-docker-nginx-and-lets-encrypt/ 

第一個部分當然就是的反向代理伺服器的職責。一開始我是參考了「Setup SSL with Docker, NGINX and Lets Encrypt」這篇的做法。作者對NGINX的要求很簡單,真的只有反向代理伺服器的proxy_pass而已。

2023-0409-224704.png

http://nginx.org/en/docs/http/request_processing.html

接著我們要讓反向代理伺服器支援虛擬主機(VirtualHost)的功能。根據使用者連入的主機名稱,NGINX判斷要連到那個後端伺服器。這個設定主要是用「server_name」完成。server_name的形式支援了精確域名、正規表達式、以及星號的通用匹配符號,詳情請看「Nginx的server_name和location配置」的介紹。

2023-0409-230501.png

除了已經設定好的虛擬主機之外,如果其他人用IP連線,我會直接回傳404。不過這方法沒辦法阻止用IP連到HTTPS,暫時想不到解決方案。

2023-0409-214003.png

https://ithelp.ithome.com.tw/articles/10280840?sc=iThelpR 

如果加入了反向代理伺服器,那後端伺服器的所有流量,都會被視為是從反向代理伺服器NGINX連入的結果。為了讓流量的真實狀況傳遞給後端的伺服器,我們需要加上以下四個設定:

  • proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
  • proxy_set_header X-Forwarded-Proto  $scheme;
  • proxy_set_Header X-Real-IP $remote_addr;
  • proxy_set_header Host   $http_host;

其餘設定又是另外的問題了。詳細請參考「Day27-好用的網頁伺服器-nginx(三)」的說明。

2023-0409-233416.png

https://www.programonaut.com/setup-ssl-with-docker-nginx-and-lets-encrypt/ 

反向代理伺服器另一個重要的功能,就是搭配SSL憑證後,為後端伺服器提供HTTPS的連線方式。「Setup SSL with Docker, NGINX and Lets Encrypt」介紹了如何搭配certbot申請Let's Encrypt憑證的做法,這也是目前最主流的方式。必須注意的是,Let's Encrypt憑證期限只有90天。我建議最好每30天檢查憑證是否需要更新。


負載平衡 / Loading balancing

2023-0409-213237.png

https://www.digitalocean.com/community/tutorials/understanding-nginx-http-proxying-load-balancing-buffering-and-caching

作為一個反向代理伺服器,背後的上游伺服器當然可以不只有一個。這就是負載平衡伺服器(loading balancing)的設置。「Understanding Nginx HTTP Proxying, Load Balancing, Buffering, and Caching」這篇說明了如何搭配upstream來將流量分散給多個上游伺服器。

2023-0409-213532.png

https://www.cnblogs.com/felixzh/p/9047021.html 

在「Nginx负载均衡的4种方式 :轮询-Round Robin 、Ip地址-ip_hash、最少连接-least_conn、加权-weight=n」這篇介紹了NGINX負載平衡的四種演算法,以及調整上游伺服器的權重。其中least_conn演算法會自動將流量分配給連線數量最少的伺服器,可能是最適合大部分情況的演算法。

2023-0409-214315.png

https://ithelp.ithome.com.tw/questions/10192668

不過這邊要注意的是,上游伺服器的連線方式建議只用HTTP,不要用HTTPS。不然你就會跟「nginx 使用 reverse proxy 時upstream 設定問題」這篇的問題一樣,卡在憑證問題難以克服。


隱藏伺服器資訊 / Avoid information disclosure

mitigationkapak-5-770x428-1.jpg

https://threatmon.io/what-is-server-header-information-disclosure/ 

What is Server Header Information Disclosure?」這篇是在說明標頭透露伺服器資訊所帶來的問題。對此,我們也需要用NGINX濾掉不必要標頭資訊。

2023-0409-215244.png

https://blog.51cto.com/liuqunying/1741061

最基本的做法是利用「proxy_hide_header」隱藏特地標頭。這裡我推薦「Nginx隐藏主机信息,proxy_hide_header 与fastcgi_hide_header」這邊的做法,設定「proxy_hide_header X-Powered-By;」。X-Powered-By會暴露後端程式語言的類型與版本,沒必要的話真的不需要公開。

2023-0409-214814.png

https://www.programonaut.com/setup-ssl-with-docker-nginx-and-lets-encrypt/

最基本的設定是「server_tokens off」。根據「隱藏/修改 Nginx 的 Server Header」的說明,如果使用預設值「server_tokens on」,標頭就會顯示NGINX的版本。這顯然不是一件好事。但就算設定了「server_tokens off」,標頭依然會看到「server: nginx」的資訊。這要怎麼修改呢?

2023-0409-221213.png

https://nestealin.com/c509d9ea/

最終做法是幫NGINX加上headers_more模組,用「more_clear_headers」來移除標頭。「利用headers_more模块修改HTTP头部」這篇是用原始碼編譯的形式安裝headers_more模組,不過根據Victor Sergienko的建議,在Debian squeeze版本之後,可以直接安裝「nginx-extras」套件即可:

apt-get install nginx-extras -y

2023-0409-222703.png

設定成功後,標頭資訊幾乎都被隱藏起來了。駭客不能得知你伺服器的軟體與版本的話,就只能瞎猜測試了。

06-error-page-configurations.png

https://www.virtuozzo.com/application-platform-docs/custom-error-page/

光是這樣還不夠。NGINX如果發生錯誤,我們會在錯誤網頁上看到熟悉的NGINX錯誤畫面。我們可以參考「Custom Error Page Settings via NGINX Balancer」的做法,自訂各種錯誤網頁的內容:「error_page 403 404 500 502 503 504 /etc/nginx/conf.d/error.html;」。附帶一提,我的錯誤內容網頁只寫了「false」,跟優雅地處理錯誤完全背道而馳。


流量限制 / Limitation

再來是為了避免大量連線癱瘓伺服器的流量限制。NGINX的流量限制分成兩個方向:同時連線的數量(limit_conn),以及短時間內的連線頻率(limit_req)。

2023-0409-223543.png

https://www.tecmint.com/limit-connections-in-nginx/ 

首先先看到limit_conn這裡。limit_conn限制了同一IP在同一時間內可以進行的最大連線數量。在「NGINX 頻寬與連線數控制」的例子中,它用「limit_conn server 1000;」跟上面的limit_conn_zone搭配,限制了伺服器最多同時可接受連線數量為1000個連線。但大多情況下我們會採用「How To Limit Number of Connections (Requests) in NGINX」這篇的做法,用$binary_remote_addr來限制單一IP同時連線數量為50個以內。

2023-0409-223735.png

https://medium.com/evan-fang/nginx-rate-limiting-%E4%BD%BF%E7%94%A8limit-req-zone%E4%BE%86%E9%99%90%E5%88%B6request%E9%87%8F-f72936ebbbac

再來看到limit_req這裡。limit_req可以限制同一IP在一定時間內可以進行的連線數量。「NGINX Rate Limiting: 使用limit_req_zone來限制request量」這篇的例子限制了同一IP每秒最多只能有兩個連線。

只要limit_conn跟limit_req設定合理的話,就可以讓人類使用者正常瀏覽網頁,同時避免爬蟲機器人短時間大量連線導致伺服器負荷過大。


提高效率 / Performance Tuning

要讓作為反向代理伺服器的NGINX提高效率,主要有兩個方向可以考慮。一個是快取,另一個是壓縮。

2023-0409-231220.png

https://segmentfault.com/a/1190000019179879 

最基本的是proxy_cache。我們可以將原本要連線到後端伺服器的特定的檔案或路徑快取起來,提供給後續其他使用者使用。「Nginx 内容缓存及常见参数配置」這篇介紹了NGINX快取大部分的做法。其中我們需要關注的有三點:

  1. location後面接的網址模式。我使用的是「location ~ .*\.(html|css|xml|rss|atom|svg|eot|otf|ttf|woff|js|png|jpeg|gif|mp3|mp4|mov|ogg)$ {」,這些檔案大部分都是不太改動的靜態資源,可以直接快取。
  2. proxy_cache_path 儲存快取的檔案路徑。
  3. proxy_cache_valid 判斷是否要快取的規則。我是用「proxy_cache_valid  any 100m;」

2023-0409-231543.png

https://docs.nginx.com/nginx/admin-guide/web-server/compression/

再來我們看到壓縮功能。NGINX使用了gzip模組來進行壓縮,詳細設定可參考「Compression and Decompression」。但要注意的是,只有符合「gzip_types」的檔案類型才能被壓縮。

2023-0409-231740.png

https://blog.longwin.com.tw/2018/10/nginx-gzip-deflate-setup-test-2018/

Nginx 的 Gzip 設定 與 測試」這篇列出了gzip模組常用的相關設定。上面有用了「gzip_types *;」來壓縮所有可能的檔案,是個聰明的做法。其中有人認為「gzip_comp_level」壓縮程度設到中等6即可,9太耗費效能而且多餘了。其中「gzip_vary on;」可以根據使用者的瀏覽器是否具備支援gzip功能來決定要回傳的檔案類型,是一定要加上去的設定。

2023-0409-232531.png

https://www.modpagespeed.com/doc/build_ngx_pagespeed_from_source

本來我還想要裝PageSpeed模組。但一來是裝不起來,二來是研究後發現PageSpeed激進的調整可能會導致更很多問題,最後就放棄不用。


結語 / Conclusion

以往談到NGINX,大多時候都只看到零星、個別的設定。既然NGINX作為反向代理伺服器是一個主要的用法,那我們應該用更全面的角度來看待NGINX的各種設定。

本篇從反向代理、負載平衡、隱藏伺服器資訊、流量限制、提高效率五個層面來規劃NGINX作為反向代理伺服器的功能。可以看到NGINX功能強大的程度,遠遠超越了我很久以前用的pound。儘管NGINX的設定有一定程度的入門門檻,但只要能夠掌握NGINX的各種特性,架設功能完善的反向代理伺服器,它不僅能夠為後端伺服器提供HTTPS連線、隱藏後端伺服器的資訊、限制流量避免DDoS攻擊,利用快取和壓縮提高伺服器的運作效率。

https://github.com/pulipulichen/docker-HTTPS-Reverse-Proxy

如果你還是覺得要架設NGINX反向代理伺服器很困難的話,不妨用看看我整理的docker-HTTPS-Reverse-Proxy吧。


最後本篇的問題是:你聽過反向代理伺服器嗎?你在哪裡聽過的呢?

知道或不知道都舉個手吧~