【筆記】Next.js 部署至 Ubuntu 並使用 apache 進行反向代理

前言

最近使用了 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 了 😅。

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments