搜索全站
建站 21 分钟阅读

WordPress联系我们页面:地图接入、表单配置与防骚扰

联系页是转化最后一环,却常被做成一张图片。本文给出完整实现:多联系方式的可点击布局、国内地图接入方案、表单配置与 SMTP、以及频率限制与人机验证的防骚扰代码。

U
UFO_PF 2024-07-31 · 阅读 21 分钟
Wo 建站
阅读提示
本文约 8,323 字,预计阅读 21 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:客户想打电话,却在手机上打不了

一次可用性测试里,访客在联系页停留了 40 秒,最后关掉页面。事后问他为什么不联系,答案是:「看到电话了,但手机上点不动,得记下来再切到拨号界面。」页面上那个电话号码是纯文字,甚至还是一张图。

联系页是企业站唯一以「转化」为唯一目标的页面,但它经常是全站最粗糙的一页。常见问题包括:电话不能点击拨号、地址是一张截图(无法复制、无法导航)、只有一个表单(不同需求的人不知道该填什么)、以及表单提交后没有任何反馈,或者更糟——表单根本收不到邮件,客户以为发成功了,其实邮件被判成垃圾邮件丢掉了。

这一页要做的事其实很明确:让不同偏好的访客(想打电话的、想发邮件的、想上门的、想加微信的)都能在一步之内达成,同时保证提交的请求真的能到达你手里,且垃圾提交不会淹没真实线索。

原理分析:三个失败点分别在哪

失败点 表现 根因
联系不上 电话不能拨、地址不能导航 纯文本或图片,缺 tel: / 地图链接
收不到 访客说发了,你没收到 服务器 mail() 被判垃圾或被拒
全是垃圾 每天几十条广告提交 表单无验证、无限流

第二个失败点在国内服务器上尤其常见。WordPress 默认用 PHP 的 wp_mail()(底层是 mail()),在没有正确 SPF/DKIM 配置的主机上发出的邮件,到达率极低。解决办法不是换插件,而是改用 SMTP 发信——WP Mail SMTP、Post SMTP 这类插件就是干这个的,配置后由专业邮件服务代发。

第三个失败点需要组合手段。垃圾提交分两类:机器批量提交和人工广告。前者靠人机验证(Cloudflare Turnstile、hCaptcha、reCAPTCHA)拦截;后者靠内容过滤(Akismet)和限流。只装验证码是不够的——很多垃圾提交来自真实人工,验证码拦不住。

地图方面,国内站点要注意:Google Maps 在国内不可访问,必须用高德或百度。两者都需要申请 key,且按调用量计费或有限额。如果不想引入第三方 JS(它会影响 LCP 与隐私合规),用「静态图 + 跳转链接」是更轻的方案。

保姆级实操

步骤 1:多联系方式的可点击布局

<?php
// 联系信息统一放一个函数,模板与页脚复用
function mmf_contact_info() {
    return array(
        'phone'    => '400-800-1234',
        'mobile'   => '13800138000',
        'email'    => 'sales@example.com',
        'wechat'   => 'https://example.com/uploads/wechat-qr.jpg',
        'address'  => '上海市松江区某某路 100 号 3 号楼',
        'hours'    => '周一至周六 9:00–18:00',
        'map_url'  => 'https://uri.amap.com/marker?position=121.23,31.03&name=某某科技',
    );
}

模板里这样输出,注意 tel: 与 mailto: 协议:

<?php
$info = mmf_contact_info();
?>
<ul class="contact-list">
    <li>
        <span class="label">销售热线</span>
        <a href="tel:<?php echo esc_attr( preg_replace( '/[^\d+]/', '', $info['phone'] ) ); ?>">
            <?php echo esc_html( $info['phone'] ); ?>
        </a>
        <span class="hint"><?php echo esc_html( $info['hours'] ); ?></span>
    </li>
    <li>
        <span class="label">邮箱</span>
        <a href="mailto:<?php echo esc_attr( $info['email'] ); ?>">
            <?php echo esc_html( antispambot( $info['email'] ) ); ?>
        </a>
    </li>
    <li>
        <span class="label">地址</span>
        <address><?php echo esc_html( $info['address'] ); ?></address>
        <a class="btn-map" href="<?php echo esc_url( $info['map_url'] ); ?>" target="_blank" rel="noopener">
            查看地图 / 导航
        </a>
    </li>
    <li>
        <span class="label">微信</span>
        <img src="<?php echo esc_url( $info['wechat'] ); ?>" alt="客服微信二维码" width="120" height="120" loading="lazy">
    </li>
</ul>

antispambot() 是 WordPress 自带函数,把邮箱转成 HTML 实体,能防住大部分采集器。二维码图片必须给 width/height,否则会贡献 CLS。

步骤 2:地图接入——轻量方案优先

优先用「跳转链接 + 静态图」,零 JS 依赖:

<a class="map-card" href="https://uri.amap.com/marker?position=121.23,31.03&name=某某科技"
   target="_blank" rel="noopener">
    <img src="https://example.com/uploads/static-map.jpg"
         alt="公司位置示意图" width="640" height="360" loading="lazy">
    <span class="map-card__cta">打开地图导航</span>
</a>

确实需要交互式地图时,再引入高德 JS API:

<?php
add_action( 'wp_enqueue_scripts', 'mmf_map_assets' );
function mmf_map_assets() {
    // 只在联系页加载
    if ( ! is_page( 'contact' ) ) {
        return;
    }

    $key = defined( 'AMAP_WEB_KEY' ) ? AMAP_WEB_KEY : '';
    if ( ! $key ) {
        return;
    }

    wp_enqueue_script(
        'amap',
        'https://webapi.amap.com/maps?v=2.0&key=' . rawurlencode( $key ),
        array(),
        '2.0',
        true
    );
    wp_add_inline_script( 'amap', '
        document.addEventListener("DOMContentLoaded", function () {
            var el = document.getElementById("amap-container");
            if (typeof AMap === "undefined" || !el) { return; }
            var map = new AMap.Map(el, { zoom: 15, center: [121.23, 31.03] });
            new AMap.Marker({ position: [121.23, 31.03], map: map });
        });
    ' );
}

key 通过 wp-config.php 里的常量注入,不要硬编码在主题里,也不要出现在日志中。is_page() 的条件加载是必须的——全站加载地图 SDK 会显著拖慢首屏。

步骤 3:表单与发信配置

# 表单插件与留痕
wp plugin install contact-form-7 --activate
wp plugin install flamingo --activate          # 提交留痕,邮件丢了也能查
wp plugin install wp-mail-smtp --activate      # SMTP 发信
wp plugin install akismet --activate           # 垃圾内容过滤

Flamingo 是关键一环:它把每次提交存进数据库,即使邮件没发出去,线索也不会丢。SMTP 插件配置好后,务必用插件自带的「发送测试邮件」验证一次再上线。

步骤 4:人机验证

Cloudflare Turnstile 目前体验最好(无图片选择、无追踪),配合 CF7 使用:

<?php
// 在表单模板里插入 Turnstile 挂件
add_filter( 'wpcf7_form_elements', function( $form ) {
    if ( false !== strpos( $form, 'cf7-turnstile' ) ) {
        return $form;
    }
    $widget = '<div class="cf7-turnstile cf-turnstile" data-sitekey="' . esc_attr( TURNSTILE_SITE_KEY ) . '"></div>';
    return str_replace( '[submit', $widget . '[submit', $form );
} );

并在 wp_enqueue_scripts 里条件加载 Turnstile 的脚本,只在含表单的页面加载。

步骤 5:频率限制(拦人工垃圾最有效的一招)

<?php
add_filter( 'wpcf7_spam', 'mmf_cf7_rate_limit', 10, 2 );
function mmf_cf7_rate_limit( $spam, $submission ) {
    if ( $spam ) {
        return $spam;
    }

    $ip = wp_remote_addr();
    if ( ! $ip ) {
        return $spam;
    }

    // 同一 IP 60 秒内只能提交一次
    $key   = 'mmf_cf7_' . md5( $ip );
    $count = (int) get_transient( $key );

    if ( $count >= 1 ) {
        $submission->add_spam_log( array(
            'agent'  => 'mmf-rate-limit',
            'reason' => '提交过于频繁(60 秒内已提交)',
        ) );
        return true;
    }

    set_transient( $key, $count + 1, 60 );

    // 同一 IP 一天最多 5 次
    $day_key   = 'mmf_cf7_day_' . md5( $ip );
    $day_count = (int) get_transient( $day_key );
    if ( $day_count >= 5 ) {
        $submission->add_spam_log( array(
            'agent'  => 'mmf-daily-limit',
            'reason' => '当日提交次数超限',
        ) );
        return true;
    }
    set_transient( $day_key, $day_count + 1, DAY_IN_SECONDS );

    return $spam;
}

wpcf7_spam 是 Contact Form 7 官方提供的过滤器,返回 true 即判定为垃圾,不会发信但会记录(Flamingo 里也能看到)。用 get_transient() 计数不需要额外建表。

步骤 6:蜜罐字段(零成本拦机器)

<?php
// 表单里加一个人类看不见的字段
add_filter( 'wpcf7_form_elements', function( $form ) {
    $honeypot = '<div style="position:absolute;left:-9999px;" aria-hidden="true">'
        . '<label>请勿填写<input type="text" name="mmf-website" value="" tabindex="-1" autocomplete="off"></label>'
        . '</div>';
    return str_replace( '[submit', $honeypot . '[submit', $form );
} );

add_filter( 'wpcf7_spam', function( $spam, $submission ) {
    if ( ! $spam && ! empty( $_POST['mmf-website'] ) ) {
        return true;
    }
    return $spam;
}, 9, 2 );

用 left:-9999px 而不是 display:none——部分机器人会跳过隐藏元素,但对真实用户来说两者都不可见。

步骤 7:提交后的反馈与追踪

表单提交成功后跳转到一个「感谢页」,而不是只显示一行文字:

<?php
add_action( 'wp_footer', function() {
    if ( ! is_page( 'contact' ) ) {
        return;
    }
    ?>
    <script>
    document.addEventListener( 'wpcf7mailsent', function ( e ) {
        window.location.href = '/thank-you/';
    }, false );
    </script>
    <?php
} );

感谢页上再放三条引导:下载产品手册、查看案例、加微信。这一页是转化漏斗里最容易加分的环节。

常见踩坑

  1. 电话写成图片:无法点击拨号,移动端转化率直接砍半。必须用 tel: 链接。
  2. 不配 SMTP:wp_mail() 在多数国内主机上到达率极低,且失败无提示。装 SMTP 插件并做发送测试。
  3. 只装验证码不装留痕:验证码拦住的部分你看不到,但如果邮件发送失败,线索也一起没了。Flamingo 必装。
  4. 地图 SDK 全站加载:联系页之外的页面白白多几百 KB。必须 is_page() 条件加载。
  5. 案例: 某企业站上线三个月,销售一直说「网上没询盘」,后来检查 Flamingo 发现有 47 条提交记录,一条邮件都没收到——主机的 25 端口被封,wp_mail() 静默失败。改用 SMTP 后,一周内收到 9 条。教训:表单上线前必须用真实邮箱做端到端测试,并且装上留痕插件作为兜底。
  6. 限流 key 用了 IP 但没有考虑 CDN:站点在 Cloudflare 后面时,wp_remote_addr() 可能拿到 CDN 节点 IP,导致所有访客共享一个限流计数。此时要用 HTTP_CF_CONNECTING_IP 或 HTTP_X_FORWARDED_FOR(需确认可信)。
  7. 感谢页做了 noindex 又做跳转统计:转化数据会失真。感谢页应当可被统计工具正常追踪。

总结

联系页的四项必备:可点击的联系信息(tel: / mailto: / 地图跳转链接)、国内可用的地图(优先静态图 + 跳转,必要时高德 JS API 且条件加载)、能真正发信的表单(SMTP + Flamingo 留痕)、以及组合式防骚扰(人机验证 + 频率限制 + 蜜罐)。

上线前必做三项测试:用手机真机点一次电话确认能拨号;用真实邮箱端到端提交一次并确认收到;模拟 60 秒内连续提交两次确认第二条被拦。

页面布局要放进整站的转化路径里考量,栏目层面的位置见 企业官网标准栏目结构设计;表单与地图脚本会明显增加请求数,上线后按 WordPress主题资源精简清单 做一次条件加载检查。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。