Nginx reverse proxy and Let’s Encrypt SSL

Leo Liao | 廖鴻林
Leo Liao
Published in
9 min readApr 29, 2023

Background

當你需要起多個application server在同一台機器上,且希望綁上domain後想要它漂亮一點不帶port,或是需要做load balance,就會需要reverse proxy來幫你完成這件事。本文會用Nginx做範例,說明如何在Nginx上做reverse proxy,並使用Let’s Encrypt頒發SSL憑證並自動renew,讓你的網站同時擁有HTTPS。

Knowledge

  • Reverse Proxy

Reverse proxy的做法就是當client發一個request到一台server後,這台 server會將request轉發到其他服務裡做處裡。但對於client來說,它只知道是把這個request發送到這台server上,可以隱藏背後那些服務真正的實體位置,其餘也可以做到request cahce、load balance等功能。

  • Let’s Encrypt

Let’s Encrypt是一個免費且自動化的SSL發佈機構,非常適合在你的server上頒發SSL憑證並透過cron job來自動更新SSL憑證,現在有很多DNS代管服務都有提供線上申請讓你下載Let’s Encrypt金鑰檔案,但你就需要手動去更新到你的server上,還不如直接在server上從頭裝比較方便。

Hands on

Step1. 下載Ngin以下提供Ubuntu的作法

下載指令如下:

sudo apt update
sudo apt install nginx
sudo service nginx start

Step2. 設定DNS

在你的DNS代管上,將兩個domain指到這台server上,設定完成後可以先在瀏覽器上確認設定的domain後先加上port應該是要可以連通的,或是本來下載Nginx時會自動設定80 port為Nginx的初始頁面。

Step3. 修改Nginx設定檔監聽http 80 port

Nginx的設定檔/etc/nginx/nginx.conf裡可以看到其中一行代碼是include /etc/nginx/sites-enabled/*,可以知道Nginx會將sites-enabled這個資料夾下的所有檔案來引入並設定。

我們直接修改它的default檔案:

sudo vim /etc/nginx/sites-available/default

先針對80 port做監聽,並將server_name填上你在上一步DNS設定的兩組domain,這邊用site1.example.comsite2.example.com舉例,並分別proxy到這台server上的3000與3001 port:

server {
listen 80;
listen [::]:80;

server_name site1.example.com;

location / {
proxy_pass http://localhost:3000;
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 $http_x_forwarded_proto;
}

server {
listen 80;
listen [::]:80;

server_name site2.example.com;

location / {
proxy_pass http://localhost:3001;
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 $http_x_forwarded_proto;
}

Step4. 重啟Nginx

記得每次改完Nginx後都必須要重啟:

sudo service nginx reload

可以試試看在瀏覽器輸入你設定的domain來確定是不是通的,(這個時候還沒設定SSL,所以記得還是http)。

Step5. 下載certbot來做Let’s Encrypt SSL

下載指令如下:

apt-get update
sudo apt-get install certbot
apt-get install python3-certbot-nginx

Step6. 針對Domain來做申請SSL

將以下的指令email改成憑證要過期前會通知你的email,與後面要申請SSL的domain:

certbot certonly --manual --preferred-challenges=dns --email my@email.com --server https://acme-v02.api.letsencrypt.org/directory -d site1.example.com -d site2.example.com

輸入後會依據你給了N組的domain給你N組代碼讓你在DNS代管上新增txt紀錄,藉此來確保你是這些domain的主人。

建立後再回到terminal按下Enter就可以成功拿到憑證金鑰:

Congratulations! You have successfully enabled https://example.com and https://www.example.com 

-------------------------------------------------------------------------------------
IMPORTANT NOTES:

Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/example.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/example.com//privkey.pem
Your cert will expire on 2017-12-12.

由上面的訊息可以得知你的金鑰位置與過期時間,過期時間先不用管,之後會做自動renew,先把金鑰的位置記下來,下一步會用到。

Step7. 修改Nginx設定檔監聽https 443 port

先將所有http request轉到https上:

server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}

接著修改原本step3設定的兩組proxy,改成監聽https 443 port,並設定金曜的位置:

server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;

ssl_certificate /etc/letsencrypt/live/site1.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/site1.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;

server_name site1.example.com;

location / {
proxy_pass http://localhost:3000;
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 $http_x_forwarded_proto;
}
}

server {
listen 443 ssl http2;
listen [::]:443 ssl http2;

ssl_certificate /etc/letsencrypt/live/site2.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/site2.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;

server_name site2.example.com;

location / {
proxy_pass http://localhost:3001;
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 $http_x_forwarded_proto;
}

Step8. 重啟Nginx

記得要重啟Nginx:

sudo service nginx reload

可以在瀏覽器試試你的domain加上https,到這裡基本的設定都做完了,應該是可以看到連線是正常的!

Step9. 自動更新SSL憑證

在terminal打開cron的設定:

crontab -e

接著我們設定在每天中午12點確認SSL是否會在未來30天內過期,如果是的話就會自動更新它:

0 12 * * * /usr/bin/certbot renew --quiet

當你順利走到這步,你就已經完成所有的設定啦🥳

Conclusion

一直以來都沒有自己設定過Nginx proxy和SSL的設定,終於有這個機會在自己的side project上用到。雖然現在有非常多的線上服務可以幫你做proxy和SSL發佈,但總歸背後都是這些基本的概念所組成的,所以寫下這篇文章來記錄,如果未來的我有這類型的設定需求時,再來回憶一下吧!

如果你喜歡這篇文章或是這篇文章有幫助到你的話,不要吝嗇給我一些👏吧!

--

--

Leo Liao | 廖鴻林
Leo Liao

Frontend Engineer | Web Developer,覺得分享經驗就跟潛水一樣,不知不覺在每段旅途中多認識了自己一點