表單寄不出?網站工程師 Debug 快速檢查清單

我猜你會看到這篇文章,多半正在處理「表單按了送出卻寄不出」的現場問題,或準備替客戶做一次寄信健檢。
這篇文章適合:網站工程師、維運人員、技術 PM。焦點鎖定發信端(網站/主機/程式),用可直接複製的步驟把問題切半、定位並修復。

你會拿到:5 分鐘快篩流程、Windows CMD/OpenSSL/swaks 檢測指令、PHPMailer 測寄腳本、常見錯誤碼對照、實務 Checklist。
不涵蓋範圍:已成功寄出但收不到(垃圾信匣、Outlook 規則、企業防火牆、群組轉寄遺失)。

此類將在下一篇〈表單收不到信〉專文處理,以免混淆。

先快篩:5 分鐘判斷「寄不出」還是「收不到」

目標:用最少步驟把問題切半,避免在錯誤方向耗時。

步驟

📤 表單寄信失敗?先按這些步驟排查:

1
用兩個外部信箱交叉測:
各送一次到 Gmail 與 Outlook(不同網域、不同反垃圾機制)。
2
開啟 DevTools → Network:
提交表單時檢查 API 回應碼與 Response 內容:
2xx 但沒收到信 → 可能是收件端(下一篇)
4xx/5xx 或 Response 內有錯誤訊息 → 多半是寄不出(本篇範圍)。
3
伺服器側做「系統級測寄」:
用 swaks 或 SMTP 指令測試(共享主機可改用 PHPMailer 測寄腳本)。
4
比對測試結果:
系統級測試也失敗 → 問題可能在主機 / SMTP / DNS 層面
系統級成功、唯獨網站表單失敗 → 程式/外掛/Token 權限等問題
5
查看郵件佇列與 Error Log:
查看 Postfix / Exim 佇列、web error log 中是否有錯誤碼(如 535/550/554/421)、TLS 握手失敗等記錄。

輸出

  • 一張簡記:測試時間、收件信箱、API 回應、伺服器日誌截圖 → 方便與客戶或同事同步、避免重複排查。

使用者環境:最容易被忽略的第一層

很多「看起來送了但其實沒送」都卡在這層。

常見狀況

  • 瀏覽器快取 / Cookie 過期:CSRF/Nonce 過期,回傳 419/403;或舊 JS 綁在快取裡,導致提交前就被攔。
  • 擋第三方腳本或隱私外掛:reCAPTCHA、Turnstile、或前端驗證腳本被擋,按下送出其實沒觸發。
  • 公司網路限制:企業 Proxy/防火牆阻擋 POST、或把表單域名列入黑名單,造成提交逾時。
  • 時區/時間不同步:Token 驗證依賴時間戳,使用者端時間錯位會被判無效。
  • 前端 JS 錯誤:Console 報錯導致 submit 被中斷(尤其 SPA/重前端網站)。

如何檢查

  • 無痕視窗+停用瀏覽器外掛再測一次;不同瀏覽器再測一次。
  • DevTools → Console/Network:
    • Network 觀察 POST 是否發出、回應狀態碼、Response 文字。
    • Console 是否有 JS error(跨網域、CORS、CAPTCHA 未通過)。
  • 若網站有 CAPTCHA:切到網路環境(行動網路)重測,排除公司網路限制。
  • 伺服器端對應時區(PHP date.timezone)與頁面 Token 有沒有過期判定過嚴。

快速修正

  • 清快取、刷新 Nonce;必要時調整 Token TTL。
  • 對前端驗證加上「失敗提示」與退回訊息,避免使用者無感卡住。
  • 提供後備提交(純 HTML 表單或簡單 POST)作為故障保險。

後台郵件設定錯誤:不存在的信箱會「拖累整批」

這是最常見且最容易被忽略的實務地雷。

典型地雷

  • 寄件人 From 使用不存在的信箱(如 noreply@yourdomain.com 但實際沒建立):
    • 很多 SMTP/ESP 會拒收或降權,甚至 DMARC 政策會直接擋。
  • 混用外部網域:用 From: xxx@gmail.com 從你自家網域主機送,SPF/DKIM 對不上,容易被退。
  • 同封信群發多收件人:To/Cc/Bcc 只要有一個不存在/格式錯,整批可能報錯或中止。
  • Reply-To/Return-Path 配置不當:回信與退信無法正確回到可監控的信箱,導致誤判為「沒寄出」。
  • 群組/轉寄規則繞來繞去:在寄件端看似正常,但實際被轉寄機制丟失或退回(尤其混用 Google Group/Exchange)。

標準作法(工程師版 SOP)

  • From 必須是同網域、且存在、且可驗證(真的建立信箱或使用 SMTP 提供商核可的 Sender)。
  • Reply-To 才放客服信箱(可與 From 不同,但要可收信)。
  • 逐收件人投遞:程式層「一封一收件人」發送,紀錄每一筆 send result,任何一筆失敗都能精準定位。
  • 建立「退信信箱」(Return-Path/Envelope-From)並監控,退信樣板會告訴你是不存在、滿信箱、或策略性拒收。
  • 檢核格式:所有收件人 email 先通過 regex/簡單 MX check;避免單一錯誤拖累整批。

程式與主機層:函式、埠號、TLS 與版本相容性

典型地雷

  • 寄送方法搞不清:後台以為走 SMTP,其實程式還在用 mail()。共享主機常預設關閉 mail() 或改用 sendmail 路徑,造成靜默失敗。
  • 埠號與加密錯誤
    • 465=Implicit TLS、587=STARTTLS、25 常被 ISP 擋。
    • 465 卻選 STARTTLS、或 587 卻選 SSL/TLS,會握手失敗。
  • OpenSSL / cURL / PHP 過舊:伺服器僅支援 TLS1.0/1.1,收信端要求 TLS1.2+ 時,會出現 sslv3 alert handshake failureno shared cipher
  • 主機權限與封鎖sendmail_path 不存在、FPM 權限不足、SELinux/AppArmor 擋路、或主機商限制外寄速率(Rate limit / Throttle)。
  • 附件與編碼:過大(常見 10–25MB 上限)、中文檔名編碼錯誤、或 Content-Type 不正確導致拒收。
  • 外掛相衝:兩個 SMTP/郵件外掛同時啟用,Hook 被覆蓋;或 CMS/外掛版本不相容。

怎麼測

  • 直接測 TLS/埠號
# 測 587(STARTTLS)
openssl s_client -starttls smtp -connect smtp.example.com:587 -crlf

# 測 465(Implicit TLS)
openssl s_client -connect smtp.example.com:465 -crlf
  • 成功=主機/SMTP 正常;若前端仍失敗=鎖定程式/外掛層。
  • 看日誌
    • Web/PHPerror_log、FPM log;搜尋 SMTP, PHPMailer, fopen, stream_socket_enable_crypto.
    • MTA(Postfix/Exim):
      • Postfix:/var/log/maillog/var/log/mail.log
      • Exim:/var/log/exim_mainlog
    • 雲郵件服務(SendGrid、Amazon SES 等):查事件活動紀錄(Accepted/Deferred/Bounced/Blocked)。

常見錯誤碼對照(寄件端常見)

  • 535 5.7.8 認證失敗(帳密/機制不符、或需應用密碼/Token)。
  • 550 5.1.1 收件位址不存在。
  • 554 5.7.1 政策性拒收(黑名單、SPF/DKIM/DMARC 不符、或內容被判垃圾)。
  • 421 4.7.0 服務暫時不可用/超限,稍後重試。
  • 5.7.26 常見於 Google:DMARC 對齊失敗,被策略拒收。

DNS 與郵件驗證:SPF、DKIM、DMARC、PTR 是關鍵

你要的最小合格組合

  • From 網域可驗證且存在(真的有信箱或核可的寄件身分)。
  • SPF:授權「實際寄信的伺服器或服務」。避免 +all,建議 ~all-all
  • DKIM:為 From 的網域 簽章;選好 selector,TXT 正確發布。
  • DMARCv=DMARC1; p=none/quarantine/reject; rua=...;確保 對齊(Alignment)成立(adkim/aspf 通常設 sr)。
  • PTR(反解):寄件 IP 需有正確 rDNS,HELO/EHLO 主機名與 PTR/主機名一致性越高越不易被打分。

快速檢查指令

REM 查 SPF / DKIM / DMARC(TXT 紀錄)
nslookup -type=TXT yourdomain.com
nslookup -type=TXT default._domainkey.yourdomain.com
nslookup -type=TXT _dmarc.yourdomain.com

REM 查 MX(有些收信端會檢查 From 網域基本健全性)
nslookup -type=MX yourdomain.com

REM 查寄件 IP 的 PTR(反解)
nslookup -type=PTR 203.0.113.10

常見錯誤與修正

  • SPF 重複或過長:多家 ESP 疊加 include:,超過 10 次查詢上限會失效。→ 合併紀錄、用子網域分流。
  • DKIM selector 放錯網域:簽 mail.yourdomain.com,卻把 TXT 發在根網域或反之。→ 對齊 selector 與 d= 參數。
  • DMARC p=reject 但未對齊:直接進拒收。→ 先用 p=none 收集報告,調整對齊再升級策略。
  • PTR 為通用名稱:如 123-45-6-7.isp-pool.example。→ 申請自訂反解或使用合規寄信服務。
  • 新 DNS 尚未全球傳播:TTL 太長或剛改完就測。→ 降 TTL,給 15–30 分鐘至數小時緩衝。

工程師實務檢測清單

目標:把問題「切半」→「再切半」,10–30 分鐘內定位八九成根因。

A. 前端與環境

  • 無痕視窗、停用瀏覽器外掛重測,觀察 DevTools Network/Console。
  • 換網路(4G/5G)排除公司 Proxy/防火牆阻擋。
  • CAPTCHA 是否顯示並通過回傳。
  • 確認 CSRF/Nonce 是否過期;必要時放寬 TTL。

B. 後台設定與收件配置

  • From 必須為同網域且存在的信箱;Reply-To 才放客服/對外信箱。
  • 逐收件人獨立投遞並記錄每筆結果,避免一人錯拖累全批。
  • 建立並監控退信信箱(Return-Path),保存退信樣本。
  • 檢核所有收件位址格式與 MX 存在性(可做輕量驗證,不主動對方 SMTP 驗箱)。

C. 程式與主機

  • 確認「實際」使用的寄送方法:mail()sendmail、SMTP、或 ESP API。
  • 檢查 PHP/外掛版本;避免雙 SMTP 外掛相衝。
  • 測埠與 TLS:openssl s_client 驗證 465/587;設定與服務端能力一致。
  • 系統級測寄:swaks 或最小 PHPMailer 腳本測試。
  • 檢查主機限制:Rate limit、sendmail_path、FPM 權限、SELinux。
  • 檢查附件大小與檔名編碼。

D. DNS 與驗證

  • SPF 僅授權實際寄件來源;避免超過 10 次 DNS lookup。
  • DKIM 簽章與 selector、d= 網域一致;DNS TXT 正確。
  • DMARC 先 p=none 蒐集報告,再逐步升級至 quarantine/reject
  • 寄件 IP 具備正確 PTR;HELO 名稱與 rDNS/hostname 合理一致。

E. 日誌與證據

  • 蒐集時間戳、請求 ID、API Response、伺服器/郵件日誌片段。
  • 彙整錯誤碼(535/550/554/421/5.7.26)與對應修正。
  • 交叉驗證 Gmail 與 Outlook 測寄結果,保存原始信件標頭(Received/SPF/DKIM/DMARC)。

F. 驗收標準

  • 連續三次跨網域(Gmail、Outlook)皆成功收件且通過 SPF/DKIM/DMARC。
  • 表單、系統級測寄結果一致。
  • 退信率與暫拒(4xx)比例顯著下降,無握手與認證類錯誤。

範例與模板:最小可行測寄腳本與日誌判讀

A) 最小 PHPMailer 測寄腳本(test_smtp_send.php

用來「繞過前端與外掛」,直接確認主機與 SMTP 是否健康。請先 composer require phpmailer/phpmailer

<?php
// test_smtp_send.php
// 目的:系統級測寄,定位主機/SMTP/TLS 是否可用
// 使用前:composer require phpmailer/phpmailer

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require __DIR__ . '/vendor/autoload.php';

$mail = new PHPMailer(true);

try {
    // 基本參數
    $mail->isSMTP();
    $mail->Host       = 'smtp.example.com';     // ← 你的 SMTP
    $mail->SMTPAuth   = true;
    $mail->Username   = 'sender@example.com';   // ← 你的帳號
    $mail->Password   = 'your_app_password';    // 建議用應用程式密碼或 API Key
    $mail->Port       = 587;                    // 587=STARTTLS, 465=Implicit TLS
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // 若用 465 改為 ENCRYPTION_SMTPS

    // Debug(只在測試時開)
    $mail->SMTPDebug  = 2; // 0=關閉, 2=詳細握手
    $mail->Debugoutput = 'error_log'; // 輸出到 PHP error_log

    // 寄件人與收件人
    $mail->setFrom('sender@example.com', 'SMTP Health Check'); // 必須為存在且同網域的信箱
    $mail->addAddress('you@gmail.com', 'Test Receiver');       // 測向 Gmail 或 Outlook
    // $mail->addAddress('you@outlook.com');

    // 建議明確設定回覆與退信地址
    $mail->addReplyTo('support@example.com', 'Support');
    $mail->Sender = 'bounce@example.com'; // Return-Path(需存在,方便收退信)

    // 內容
    $mail->isHTML(true);
    $mail->Subject = 'SMTP pipeline ok';
    $mail->Body    = 'This is a minimal SMTP test via PHPMailer.';
    $mail->AltBody = 'Minimal SMTP test.';

    $mail->send();
    echo "OK: message accepted by SMTP.\n";
} catch (Exception $e) {
    echo "FAIL: {$mail->ErrorInfo}\n";
}

如何判讀

  • 結果顯示 OK: message accepted by SMTP. → 主機與 SMTP 正常;若表單仍失敗=程式/外掛/權限層。
  • 若握手或認證錯誤,SMTPDebug 會顯示關鍵線索(TLS 失敗、535 Auth、超時等)。

B) 系統級 SMTP 測試(swaks

swaks --to you@receiver.com --server smtp.example.com \
      --port 587 --auth LOGIN --auth-user "sender@example.com" \
      --auth-password '********' --tls \
      --header "Subject: hello from swaks" \
      --body "SMTP pipeline ok"
  • 成功 → SMTP 端無誤;失敗訊息可比對錯誤碼(535/550/554/421)。

C) 郵件標頭判讀要點(收件端觀察)

收到測試信後,開啟「原始郵件」檢視標頭,重點看:

Authentication-Results: ... spf=pass ... dkim=pass ... dmarc=pass
Received: from smtp.example.com ...
  • spf=pass dkim=pass dmarc=pass → 驗證齊備,之後收件穩定度高。
  • 缺一常致投遞品質下滑;若 dmarc=fail 且對方政策嚴格,可能直接拒收。

D) 日誌關鍵字與範例

Web/PHP

  • stream_socket_enable_crypto(): SSL operation failed → TLS 參數或版本不相容。
  • Connection could not be established with host → DNS/Port/防火牆或主機封鎖。
  • SMTP Error: Could not authenticate → 535 認證錯;常見是應用密碼未啟用或機制不符。

Postfix(/var/log/mail.log)

  • status=sent (250 2.0.0 Ok: queued as ...) → 投遞成功
  • status=deferred (421 4.7.0 ...) → 暫拒,稍後重試
  • status=bounced (550 5.1.1 User unknown) → 收件人不存在

Exim(/var/log/exim_mainlog)

  • <= 收信、=> 投遞、** 退信
  • H=mx.receiver.com [x.x.x.x] rejected RCPT <user@...>: Recipient address rejected: User unknown → 550

E) 寄件策略小模板(工程實務)

  • 單筆投遞:一封對一收件人,成功與否逐筆記錄,避免一人錯拖累全批。
  • 退信管道:固定以 bounce@yourdomain.com 為 Return-Path,解析退信原因,自動重試可恢復的 4xx。
  • 事件追蹤:紀錄 request-id、message-id、SMTP response;與 GA4 或後台 log 連動,形成可追溯閉環。

表單「寄不出」大多不是單點錯,而是環境 → 後台 → 程式 → DNS/郵件驗證 → 收件端多層交互的結果。照著本文的快篩與檢測清單,把問題一層一層切半,多數情況能在 10~30 分鐘內定位八九成根因;接著用 SOP 模板把修正做紮實,之後的寄送會穩定許多。

FAQ|表單寄不出:工程師常見問題速查

卡關了嗎?

錯誤碼、發信時間、網域與 SMTP 設定 貼給我,
我陪你一起 Debug。

立即聯絡技術支援

返回頂端