前言:一個聯絡表單,牽出一次 PHP 升級
原本只是想在網站加一頁聯絡表單,安裝 Contact Form 7 時卻跳出「需要較新版本的 PHP」。新版 Contact Form 7(6.2.x)要求 PHP 8.3 以上,而主機還在跑 PHP 8.2。
就算不為了外掛,PHP 8.2 的官方安全支援也只到 2026 年 12 月 31 日,本來就該升了。這篇紀錄整個過程:PHP 8.2 → 8.3 平滑切換、Contact Form 7 搭配 Cloudflare Turnstile 擋機器人,以及用 Microsoft 365 OAuth 寄出表單通知信。
每個步驟都附上驗證與回滾方式,照著做可以隨時退回原狀。
環境概覽
| 項目 | 內容 |
|---|---|
| 主機 | Vultr VPS,Marketplace WordPress 映像(Nginx 版) |
| 作業系統 | Ubuntu 24.04 LTS |
| Web Server | Nginx 1.24.0 |
| PHP | 8.2.35 → 8.3.36(PHP-FPM,套件來源 ondrej/php PPA) |
| WordPress | 7.1.4 |
| 前端防護 | Cloudflare Proxy + Turnstile |
| 寄信 | Microsoft 365 Exchange Online(OAuth) |
整體流程:訪客送出表單 → Turnstile 驗證 → WordPress(PHP-FPM 8.3)處理 → FluentSMTP 透過 Microsoft API 寄出通知信。
PHP 8.2 升級 8.3:平滑切換步驟
核心思路是讓 8.2 與 8.3 並存:8.3 先裝好、設定好,最後只改 Nginx 一行就切過去。8.2 留著待命,出問題時改回那一行即可。
0. 先做 Snapshot
在 Vultr 後台對主機做一份 Snapshot,作為最後的回滾點。
1. 盤點現況
先到 WordPress 後台「工具 → 網站健康狀態 → 資訊 → 伺服器」確認網站實際使用的 PHP 版本,CLI 的 php -v 不一定相同。
apt policy php8.3-fpm # 確認 8.3 會從哪個套件來源安裝
grep -rnE "client_max_body_size|fastcgi_pass" /etc/nginx/
grep -rnvE "^\s*(;|$)" /etc/php/8.2/fpm/pool.d/ # 列出 pool 的有效設定
我的主機盤點出三個重點:
- 8.2 來自 ondrej/php PPA,所以 8.3 也從同一來源安裝,避免共用套件版本打架。
- 自訂值(上傳上限 2G、
pm.max_children等)寫在 FPM pool 的php_admin_value,不在 php.ini。 - PHP-FPM 監聽
127.0.0.1:9000,8.3 改用 Unix socket,兩版才能同時運作。
2. 安裝 8.3 並補齊模組
apt install php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd php8.3-mbstring \
php8.3-xml php8.3-zip php8.3-intl php8.3-bcmath php8.3-imagick
php8.2 -m > /tmp/old.txt; php8.3 -m > /tmp/new.txt; diff /tmp/old.txt /tmp/new.txt
diff 中 < 開頭的模組代表 8.2 有、8.3 沒有。先全部補齊,確保切換前後行為一致,之後再精簡:
apt install php8.3-bz2 php8.3-gmp php8.3-igbinary php8.3-redis php8.3-soap \
php8.3-sqlite3 php8.3-xmlrpc php8.3-mcrypt
3. 複製 pool 設定並調整
cp /etc/php/8.3/fpm/pool.d/www.conf /root/www.conf.php83-default
cp /etc/php/8.2/fpm/pool.d/www.conf /etc/php/8.3/fpm/pool.d/www.conf
sed -i \
-e 's|^listen = 127.0.0.1:9000|listen = /run/php/php8.3-fpm.sock|' \
-e 's|^php_admin_value\[post_max_size\] = 2G|php_admin_value[post_max_size] = 100M|' \
-e 's|^php_admin_value\[upload_max_filesize\] = 2G|php_admin_value[upload_max_filesize] = 100M|' \
/etc/php/8.3/fpm/pool.d/www.conf
上傳上限順手從 2G 降到 100M。網站在 Cloudflare Proxy 後面,Free 方案單次請求上限是 100MB,2G 實際上用不到,設大了只會增加風險。
4. 調整 Opcache
原本的 interned strings buffer(8MB)已經 100% 用滿,新版一併調大。設定放在獨立檔案,方便日後管理:
cat > /etc/php/8.3/fpm/conf.d/99-custom.ini <<'EOF'
opcache.memory_consumption = 192
opcache.interned_strings_buffer = 16
EOF
php-fpm8.3 -t && systemctl restart php8.3-fpm
ls -l /run/php/ # 應出現 php8.3-fpm.sock
到這一步網站仍在 8.2 上運作,完全不受影響。
5. 切換 Nginx
BK=/root/nginx-backup-$(date +%F-%H%M); mkdir -p $BK
cp -a /etc/nginx/sites-available /etc/nginx/site-extras $BK/
sed -i 's|fastcgi_pass 127.0.0.1:9000;|fastcgi_pass unix:/run/php/php8.3-fpm.sock;|' <站台設定檔>
sed -i 's|client_max_body_size 2G;|client_max_body_size 100M;|' <站台設定檔>
nginx -t && systemctl reload nginx
update-alternatives --set php /usr/bin/php8.3
<站台設定檔> 換成盤點時 grep 出來、實際啟用中的檔案。
驗證
- 網站健康狀態顯示 PHP 8.3.36、上傳上限 100M
- 前台、後台、媒體上傳都正常
tail -n 50 /var/log/nginx/error.log沒有 502 或 FastCGI 錯誤
Opcache hit rate 剛切換時會掉到 70% 左右,這是快取重新填充的正常現象,瀏覽一陣子就會回到 99%。
回滾
8.2 還在 9000 埠待命,把 Nginx 設定從備份還原再 reload 即可:
cp -a $BK/sites-available/. /etc/nginx/sites-available/
cp -a $BK/site-extras/. /etc/nginx/site-extras/
nginx -t && systemctl reload nginx
觀察幾天確認穩定後,再停用 8.2:systemctl disable --now php8.2-fpm。
Contact Form 7 + Cloudflare Turnstile
Contact Form 7 從 6.1 版開始原生支援 Cloudflare Turnstile,不需要額外外掛,貼上兩把 key 就能啟用。Turnstile 免費,大多數訪客也不需要點選圖片。
1. 建立 Turnstile widget
- Cloudflare Dashboard → Turnstile → Add widget
- Hostname 填自己的網域,Widget Mode 選 Managed(Contact Form 7 官方建議)
- 取得 Site Key 與 Secret Key。Site Key 本來就會出現在網頁原始碼,Secret Key 則要保密。
2. 在 WordPress 串接
- 安裝並啟用 Contact Form 7(作者為 Rock Lobster Inc.,網址是 wordpress.org/plugins/contact-form-7)
- 聯絡 → 整合 → Cloudflare Turnstile → Setup integration,貼上兩把 key 並儲存
存好後,所有表單都會自動加上 Turnstile。預設放在表單最上方,若想移到送出按鈕上面,在 [submit] 前一行插入 [turnstile] 即可。
3. 建立聯絡頁
- 編輯表單,在「電子郵件」分頁設定:
- 寄件者(From):自己網域、且之後 SMTP 要登入的信箱
- 其他標頭:
Reply-To: [your-email],收到信直接按回覆就會回給訪客
- 複製表單的短代碼,新增頁面並貼上,網址代稱設為
contact
驗證
用無痕視窗開啟聯絡頁,表單上方出現 Turnstile 並自動顯示「成功」,Cloudflare 的 Turnstile Analytics 也開始出現請求數。
回滾
聯絡 → 整合 → Turnstile 按 Remove keys,表單就恢復成沒有驗證的狀態。
FluentSMTP + Microsoft 365 OAuth 寄信
寄信最後選了 FluentSMTP 走 OAuth,而不是傳統的帳號密碼 SMTP。原因是 Microsoft 預計在 2026 年 12 月底,對既有租戶預設停用 SMTP AUTH 的 Basic Auth(管理員仍可手動開回),最終移除日期會在 2027 年下半年公布。新設定沒必要選即將淘汰的方式。
FluentSMTP 免費,透過 Microsoft API 寄信,不經過 SMTP AUTH。
1. 取得 Callback URL
設定 → FluentSMTP → 選 Microsoft,畫面會顯示 App Callback URL(https://你的網域/wp-json/fluent-smtp/outlook_callback),先複製起來。
2. 在 Entra ID 註冊應用程式
用要拿來寄信的那個 M365 帳號登入 Entra 系統管理中心,不要用 Global Admin,否則容易出現授權錯誤。
- App registrations → New registration
- Supported account types 依 FluentSMTP 文件選多租戶加個人帳號;Redirect URI 類型選 Web,貼上 Callback URL
- 記下 Application (client) ID
- Certificates & secrets → New client secret,複製 Value 欄位(不是 Secret ID),離開頁面後就看不到了
3. 回到 FluentSMTP 授權
填入 From Email、Client ID、Client Secret,按 Authenticate with Office 365 取得 Access Code,貼回欄位後儲存。最後記得把 Contact Form 7 的寄件者改成同一個信箱。
驗證
- FluentSMTP → Email Test 寄一封測試信
- 實際填表送出,確認收到通知信,按回覆時收件者是訪客的信箱
- FluentSMTP → Email Logs 有對應的寄送紀錄
回滾與維護
- 停用 FluentSMTP,WordPress 會回到預設的 PHP
mail() - 在 Entra ID 刪除該 App 或其 client secret,就能立即撤銷寄信權限
- Client secret 有到期日,到期前要更新,否則表單會突然寄不出信,建議在行事曆設提醒
順手修正:Rank Math 缺少資料表
檢查 PHP log 時,發現每次儲存文章都會出現 Table 'wp_rank_math_analytics_objects' doesn't exist。這跟 PHP 升級無關,是 Rank Math 的 Analytics 模組啟用時,資料表沒有建立成功。
修正方式:Rank Math SEO → 狀態與工具 → 資料庫工具 → Re-create Missing Database Tables。找不到「狀態與工具」選單時,先在 Rank Math 儀表板切換到 Advanced Mode。
驗證資料表已建立:
mysql -e "SHOW TABLES LIKE '%rank_math_analytics%'" <資料庫名稱>
這個工具只會新增缺少的資料表,不會動到既有資料。
踩坑紀錄與心得
| 狀況 | 原因 | 解法 |
|---|---|---|
| grep 結果洗滿整個終端機 | 遞迴搜尋 /var/www/ 時掃到外掛裡壓縮過的 JavaScript,一行就有幾千字 | 只搜尋設定檔目錄,或指定單一檔案 |
| php.ini 是 2M,網站卻顯示 2G | 覆寫值寫在 FPM pool 的 php_admin_value,行首比對的 grep 抓不到 | 用 grep -v 排除註解,列出 pool 全部有效設定 |
| 8.2 與 8.3 無法同時監聽 9000 埠 | 兩個 FPM 搶同一個 TCP port | 8.3 改用 Unix socket,切換與回滾都只改 Nginx 一行 |
貼指令後出現一堆 command not found | 連同上一次的表格輸出一起貼進終端機 | 只選取指令那一行;這些錯誤沒有執行任何東西 |
心得:
- 先並存再切換:新舊版本同時運作,切換只動一行設定,回滾成本幾乎是零。
- 自訂值集中管理:放在
conf.d/99-custom.ini這類獨立檔案,升級時一眼就知道要搬什麼。 - 看 log 要看時間:
tail -f會先印出最後 10 行,修好之後看到的錯誤可能是舊紀錄。記得對照時間戳,而且 log 預設是 UTC。
常見問題(FAQ)
Contact Form 7 顯示「需要較新版本的 PHP」怎麼辦?
新版 Contact Form 7(6.2.x)要求 PHP 8.3 以上。自架 VPS 可以依本文步驟升級 PHP;虛擬主機則到主機商的控制台切換 PHP 版本。
使用 Turnstile 一定要把網站搬到 Cloudflare 嗎?
不需要。只要有免費的 Cloudflare 帳號就能建立 Turnstile widget,DNS 不必託管在 Cloudflare。
Ubuntu 24.04 內建 PHP 8.3,為什麼還用 PPA?
Ubuntu 原生的 php8.3 放在 universe,版本停在 8.3.6,只靠 backport 修補。PPA 跟著上游 patch release 更新。如果原本的 PHP 已經來自 PPA,繼續使用同一來源也能避免共用套件衝突。
WordPress 寄信為什麼不用 Microsoft 365 的 SMTP 帳號密碼?
Exchange Online 的 SMTP AUTH Basic Auth 預計在 2026 年底對既有租戶預設停用。新設定建議直接走 OAuth,例如 FluentSMTP 的 Microsoft 連線。
升級後 Opcache hit rate 掉很多正常嗎?
正常。PHP-FPM 重啟後快取是空的,網站被瀏覽一段時間後就會回到 99% 左右。
參考資料
- PHP Supported Versions
- Contact Form 7 – Turnstile integration
- Cloudflare Turnstile 官方文件
- MC786329 – Exchange Online to retire Basic Auth for Client Submission (SMTP AUTH)
- FluentSMTP – Setup Outlook with FluentSMTP
- Ondřej Surý PHP PPA
- WordPress Hosting Handbook – Server Environment