首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >表单提示提交成功,后台没记录、邮件也没到:先查哪一层,日志看哪几行

表单提示提交成功,后台没记录、邮件也没到:先查哪一层,日志看哪几行

原创
作者头像
用户10345225
发布于 2026-11-01 22:15:02
发布于 2026-11-01 22:15:02
890
举报

表单页显示「提交成功」,后台翻遍没有记录,通知邮箱也没有动静。这三种现象分别属于不同的层:浏览器的请求发没发出去、应用有没有接住并落库、邮件有没有送到收件人手里。当成一个问题查,很容易在「刷缓存」和「换发信服务」之间来回猜。下面按层拆开,每层都写清楚该看哪里、命令怎么跑。

「提交成功」只是浏览器拿到了一个响应

先在浏览器开发者工具的 Network 面板找到那条提交请求,看状态码和响应体。200 加一段 JSON 成功标记,才算前端这一层通过;403 多半是 REST 的 nonce 跟着页面缓存一起过期了,429 是被限速,499 或 502 是网关在应用返回之前就断了连接。

有些表单的「成功」提示是前端脚本直接弹的,请求失败了照样显示。判断办法很简单:把响应体贴出来看,或者直接对端点发一次请求。

代码语言:bash
复制
curl -sS -o /dev/null -D - -X POST 'https://example.com/wp-json/your-form/v1/submit' \
  -H 'Content-Type: application/json' \
  --data '{"name":"selftest","email":"selftest@example.com"}'

这条命令不带 nonce,正常情况下返回 403,说明路由存在、鉴权也在生效。返回 404 说明端点根本没注册——插件没启用,或者重写规则没刷新,wp rewrite flush 跑一次再看。返回 200 反而要警惕,等于鉴权没起作用,任何人都能往里灌数据。

请求有没有到应用:access log 和 WAF 日志

浏览器报错只能说明它没收到响应,请求可能已经打到服务器又被挡回去了。先看 Web 服务器的 access log:

代码语言:bash
复制
# 最近的表单类 POST
grep -E 'POST /(wp-json|wp-admin/admin-ajax\.php)' /var/log/nginx/access.log | tail -n 30

# 状态码分布,看有没有成片的 403 / 429 / 499
grep -E 'POST /(wp-json|wp-admin/admin-ajax\.php)' /var/log/nginx/access.log \
  | awk '{print $9}' | sort | uniq -c | sort -rn

日志里连 POST 都没有,问题在客户端或中间层:CDN 边缘节点、WAF 规则、浏览器插件、公司网络代理,逐个摘掉试。日志里全是 403,先区分是 WAF 拦的还是应用拦的——WAF 拦的请求通常不会进 PHP,PHP 的错误日志里找不到对应记录,而云上的 WAF 日志能查到命中的规则 ID。日志里 499 居多,说明 PHP 处理太慢、客户端先放弃了,去看 php-fpm 的慢日志:

代码语言:bash
复制
tail -n 200 /var/log/php-fpm/error.log
grep -iE 'waf|mod_security|denied' /var/log/nginx/error.log | tail -n 50

还有一种情况是请求到了、数据也进来了,只是没进你以为的那张表。多站点装 WordPress 时 $wpdb->prefix 带站点编号,去主站表里翻自然什么都没有。

入库和发信是两条独立的链路

后台有记录、邮件没来,说明前半段是通的,问题在发信。后台没记录但 access log 里有 200,说明处理函数跑过了,只是写库失败或者写到了别处——查 PHP 错误日志里有没有 SQL 报错、事务有没有回滚。两条链路混着看,最容易把「没入库」误判成「邮件被拦」。

邮件这条路要分四段看

应用层组装的收件人:To、CC、BCC 到底是谁,从代码或插件设置里确认一遍。别急着往后查,先把这一步的证据拿到——多数「某个同事收不到」的问题,收件人列表里本来就漏了他。

发信通道:SMTP 还是 HTTP API。SMTP 看邮件服务的 maillog,API 看返回的 message id 和状态字段。发信插件一般有自己的日志表或日志文件,先确认这条通知确实交给通道了。

收件网关:企业邮箱的隔离区、垃圾邮件判定、附件大小限制。这一段只能从收件方后台看,发信方看不到。

收件人规则:过滤器、别名、转发链。这一段最容易被忽略,也最容易出「只有我收不到」。

只有某一个人收不到,同事能收到

排查顺序按这个走,别跳步:

  1. 在应用层打印实际的收件人数组,确认这个地址在不在里面。多人收件写成一行逗号分隔,还是循环逐个发,行为不一样;事务通知建议逐个发,谁的投递失败了看得清清楚楚。
  2. 到发信服务的投递日志里按这个地址查,看结果是 delivered、bounced 还是 suppressed。suppressed 表示这个地址在抑制名单里,通常是之前硬退信攒下来的,得手工移除。
  3. 让收件人看企业邮箱的隔离区和垃圾箱,以及他自己设的收信规则——把通知类邮件自动归档到某个文件夹的规则,非常常见。
  4. 这个地址是不是别名或邮件组。别名转发会重写 envelope sender,SPF 校验可能不过;DKIM 只要签名没被改动能撑住,所以别名场景下优先保证 DKIM 正确。
  5. 转发链超过一跳时,中间那一跳的退信只有转发方能看到,让收件人把原始邮件头(含 Received 行)导出来。

换了发信服务之后通知就没了

漏的通常是这几样,按顺序核:

  • 新服务要求的域名验证记录有没有发布,DKIM 的选择器是不是新的(选择器名字以服务商给的为准,不一定是 default)。
  • SPF 记录里有没有把新服务的 include 加进去。旧的 include 别留着,超过 10 次 DNS 查询会让整条记录 permerror,等于全丢。
  • DMARC 策略是 p=reject 时,新服务如果没有对齐(From 域与 DKIM 的 d= 或 SPF 的 Return-Path 同域),会被直接拒收。
  • 发件人地址的域必须是你验证过的域,别用客户的域或者免费邮箱域伪造。
  • 通道是不是还在沙箱模式、API key 权限对不对、额度有没有到顶。

验证记录用 dig 看,别只看服务商后台的绿勾:

代码语言:bash
复制
dig +short TXT example.com | tr -d '"' | grep -i spf
dig +short TXT your-selector._domainkey.example.com
dig +short TXT _dmarc.example.com

# 粗略数一下 SPF 里嵌了多少 include
dig +short TXT example.com | grep -o 'include:[^ ]*' | wc -l

退信码怎么看

4xx 是临时失败,队列里会重试,先别动配置;5xx 是永久失败,重试没有意义。

返回

含义

先查什么

5.1.1

收件人不存在

地址拼写、别名是否还有效

5.7.x

策略拒收

黑名单、SPF/DKIM 对齐、内容判定

4.7.x

临时限速或灰名单

发送频率、单次收件人数

5.2.2

邮箱满了

收件人侧,发信方无解

发信服务后台还会有两块要看:抑制名单和额度告警。抑制名单是硬退信攒出来的,不清掉的话同一地址永远发不出去;额度告警被忽略,超限之后的通知会静默失败。

发信失败之后的重试与补偿

同步发信失败就丢,是最常见的坑。WordPress 里可以挂住失败事件先落库,再用系统 cron 重试,别指望 WP-Cron——它靠访问触发,低流量站点可能一直不跑。

代码语言:php
复制
<?php
// 放进一个小插件里。先建表:created_at、recipients、subject、message、attempts
add_action( 'wp_mail_failed', function ( WP_Error $error ) {
    $data = $error->get_error_data();
    $data = is_array( $data ) ? $data : array();

    $to = $data['to'] ?? array();
    $to = is_array( $to ) ? implode( ',', $to ) : (string) $to;

    global $wpdb;
    $wpdb->insert(
        $wpdb->prefix . 'mail_failures',
        array(
            'created_at' => gmdate( 'Y-m-d H:i:s' ),
            'recipients' => $to,
            'subject'    => (string) ( $data['subject'] ?? '' ),
            'message'    => $error->get_error_message(),
            'attempts'   => 0,
        ),
        array( '%s', '%s', '%s', '%s', '%d' )
    );
} );

add_action( 'aum_retry_failed_mail', function () {
    global $wpdb;
    $table = $wpdb->prefix . 'mail_failures';
    $rows  = $wpdb->get_results(
        "SELECT * FROM {$table}
         WHERE attempts < 3 AND created_at > (UTC_TIMESTAMP() - INTERVAL 1 DAY)
         ORDER BY id ASC LIMIT 20"
    );

    foreach ( $rows as $row ) {
        $ok = wp_mail( explode( ',', $row->recipients ), $row->subject, $row->message );
        $wpdb->query(
            $wpdb->prepare( "UPDATE {$table} SET attempts = attempts + 1 WHERE id = %d", $row->id )
        );
        if ( $ok ) {
            $wpdb->delete( $table, array( 'id' => $row->id ), array( '%d' ) );
        }
    }
} );

系统 cron 里每五分钟跑一次,同时把默认的伪 cron 关掉:

代码语言:bash
复制
# wp-config.php: define( 'DISABLE_WP_CRON', true );
*/5 * * * * cd /var/www/html && /usr/local/bin/wp cron event run aum_retry_failed_mail --quiet

重试要防重复:原邮件可能其实已经发出去了,只是应用侧超时。用业务 ID 做幂等键传给发信服务,或者在表里记一个唯一键,重试前查一次。重试次数到顶还失败的,要能报警出来,否则队列就是个黑洞。

缓存、CDN、WAF 会怎么搅进来

页面被整页缓存之后,表单里的 nonce 还是旧的,用户一提交就是 403,前台却可能显示成功。判断方法是看响应头里有没有缓存命中标记和 age,然后直连源站再提交一次:

代码语言:bash
复制
curl -sS -o /dev/null -D - --resolve example.com:443:203.0.113.10 \
  -X POST 'https://example.com/wp-json/your-form/v1/submit' \
  -H 'Content-Type: application/json' --data '{"name":"selftest"}'

绕过边缘节点后正常,问题就在缓存或 CDN 配置:把表单页和 REST 路径排除出缓存。WAF 这边要注意规则会不会误伤正常内容——公司介绍里带 URL、产品型号里带引号,都可能命中 SQL 注入或 XSS 规则被拦成 403,把字段内容减到最简再试一次就能定位。

垃圾提交会不会拖累正常通知

会,路径有两条。一条是频率:机器人把提交量打上去,发信服务那边的发送速率被限,正常通知排到后面甚至被丢弃。另一条是声誉:如果垃圾提交触发了大量硬退信和投诉,发信域的评分会掉,之后所有通知都更容易进垃圾箱。

所以拦截要放在前面,别让垃圾请求走到发信那一步:加一个对用户不可见、机器人会填的隐藏字段,加一个「从打开表单到提交不少于两秒」的时间戳,再按 IP 做个每分钟的速率限制。通知邮件和营销邮件分开走不同的子域,营销那边的投诉率不会连累通知。

一份可以照着走的顺序

  1. Network 面板看那条 POST 的状态码和响应体,确认前端这层到底过没过。
  2. 用 curl 打一次端点,区分 404(路由没注册)、403(鉴权或 WAF)、200(鉴权失效)。
  3. access log 里 grep 表单端点的 POST,看状态码分布;没有记录就往 CDN 和 WAF 查。
  4. 有记录、后台没数据,查 PHP 错误日志和表前缀。
  5. 后台有数据、邮件没来,先打印收件人数组,再查发信通道日志里这条的投递结果。
  6. 结果是 delivered 却收不到,转去收件方查隔离区、垃圾箱、别名和转发。
  7. 结果是 bounced 或 suppressed,读返回码,处理抑制名单和认证记录。
  8. 最后补上失败重试、幂等和告警,让下一次出问题时至少有一条线索留在库里。

顺手想看一眼现有站点的抓取和索引状态,可以用站点检查工具。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 「提交成功」只是浏览器拿到了一个响应
  • 请求有没有到应用:access log 和 WAF 日志
  • 入库和发信是两条独立的链路
  • 邮件这条路要分四段看
  • 只有某一个人收不到,同事能收到
  • 换了发信服务之后通知就没了
  • 退信码怎么看
  • 发信失败之后的重试与补偿
  • 缓存、CDN、WAF 会怎么搅进来
  • 垃圾提交会不会拖累正常通知
  • 一份可以照着走的顺序
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档