前言
最近使用了 Next.js 重構了 2020 年用 Vue 2 開發的台鐵時刻表和 2021年用 Vue 3 的高鐵時刻表,打造新-台鐵時刻表(https://traintime.jsy.tw/),提供了台鐵/高鐵一鍵切換、亮色/暗色佈景一鍵切換以及繁中/英文一鍵切換等新功能。
只開發過靜態網站又是 Next.js 新手的我,對於第一次要部署有伺服器渲染(SSR)的網站總有些緊張。不過我並沒有使用任何 CI/CD 或容器化工具,只是單純在 Ubuntu 上運行 Next.js,所以實際操作起來並沒有想像中困難。本文中將記錄我的 Next.js 14 應用部署 Ubuntu 20 並使用 apache 進行反向代理的流程,做個筆記並供各位參考。
以下流程皆在 Ubuntu Server 上實作。
安裝 node 和 npm
若已安裝過的人可略過此過程。
sudo apt update
sudo apt install nodejs npm
node -v
npm -v
準備 Next.js 應用
假設我將 Next.js 應用上傳至以下路徑: /var/www/html/my-next-app
。
準備 Next.js 應用的生產環境版本
cd 至 Next.js 應用的路徑,安裝 node_modules 和建構生產環境版本。
cd /var/www/html/my-next-app
npm install
npm run build
接下來,就要準備在 server 上運行 Next.js 應用。
Next.js 使用 node 運行,我使用 pm2
進行 node 的管理。
安裝 pm2
sudo npm install -g pm2
新增 node 設定檔
在 Next.js 應用的根目錄中新增一個 node 設定檔(ecosystem.config.js
),以便 pm2 管理。
module.exports = {
apps: [
{
name: "my-next-app",
script: "npm",
args: "start",
env: {
NODE_ENV: "production",
PORT: 3000, // 自行決定 next app 的 port
},
},
],
};
使用 pm2 啟動應用
pm2 start ecosystem.config.js
到目前為止,Next.js 專案雖然可被正常運行,但我們還無法透過瀏覽器瀏覽到我們的網頁。
這是因為瀏覽器瀏覽預設是使用 80 或 443 port,我們剛剛在 ecosystem.config.js 裡設定的 next app port 號是 3000,因此我們必需設定 web server 的反向代理,讓 80 或 443 指到 3000 port。通常可以使用 nginx 或 apache 來達成,本文中我們是使用 apache 示範。
安裝 apache
sudo apt update
sudo apt install apache2
安裝 apache 反向代理模組
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod rewrite
設定 apache 虛擬主機
apache 針對各網域的設定檔預設位於 /etc/apache2/sites-available/
下。
sudo vim /etc/apache2/sites-available/my-next-app.conf
注意,我的網站有強制讓 user 瀏覽 443 port 無 www 網域的設定。
若沒有此需求,請專注在反向代理的部份。
以下假設網址為 https://mynextapp.jsy.tw
。
<VirtualHost *:80>
ServerName mynextapp.jsy.tw
ServerAlias mynextapp.jsy.tw
RewriteEngine on
RewriteCond %{SERVER_NAME} =mynextapp.jsy.tw [OR]
RewriteCond %{SERVER_NAME} =mynextapp.jsy.tw
RewriteRule ^ <https://mynextapp.jsy.tw>%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName mynextapp.jsy.tw
ServerAlias www.mynextapp.jsy.tw
# === 反向代理 start ===
# 將 <https://mynextapp.jsy.tw/> (443 port) 轉至 next app 的 port
# next app 的 port 在上面的 ecosystem.config.js 設定
ProxyPreserveHost On
ProxyPass / <http://localhost:3000/>
ProxyPassReverse / <http://localhost:3000/>
# === 反向代理 end ===
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
RewriteEngine on
RewriteCond %{SERVER_NAME} =www.mynextapp.jsy.tw
RewriteRule ^ <https://mynextapp.jsy.tw>%{REQUEST_URI} [END,NE,R=permanent]
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/mynextapp.jsy.tw/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mynextapp.jsy.tw/privkey.pem
</VirtualHost>
</IfModule>
重啟 apache
重啟 apache server 以讓設定生效。
sudo a2ensite nextjs-app.conf
sudo systemctl reload apache2
設定 HTTPS 憑證和 DNS record(Optional)
如果 https://mynextapp.jsy.tw
是新的網域,不要忘記設定 https 憑證和 DNS record。
可參考我之前的文章:【筆記】從加入新的 Virtual Host 子網域到建立 SSL 憑證的相關指令
設定 Next.js 應用 /api 的 endpoint(Optional)
若網頁中會向本地發送 API 請求,假設本地 API 的 port 開在 3001
,在開發階段,Next.js 應用的 /api 路由可能會這樣設定:
const response = await fetch(
`localhost:3001/api/getTrainTimeTable`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
// ...
},
);
別忘記改為 server 本地的 web API 的 port。
防止 Next.js 應用被搜尋引擎找到(Optional)
這是我個人在測試時的需求。
可以在 Next.js 應用根目錄的 /public 資料夾裡建立 robots.txt:
User-agent: *
Disallow: /
設定完別忘記重新啟動 Next.js 應用:
pm2 restart my-next-app
後記
很慶幸我的第一個 Next.js 應用可以順利上線。
下次有機會的話,再來試試 CI/CD 和 Docker 吧!不過到時候就得升級 server 的 memory 了 😅。