You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:wp_mail()自动执行确认URL致自定义文章自动发布

Hey there! Let's dig into this frustrating issue you're facing—your confirmation URL is being triggered automatically right after sending the email, which is turning your draft posts into published ones before users even get to click the link. Here's what's going on and how to fix it:

1. Why This Is Happening

Most email clients, anti-spam services, or link preview tools automatically crawl GET URLs in emails to check for malicious content or generate previews. If your confirmation link uses a simple GET endpoint (like yoursite.com/publish-post?post_id=123), these automated systems are hitting it before the user even sees the email—triggering your publish logic prematurely.

2. Actionable Fixes to Stop Auto-Triggering

Let's walk through robust solutions, starting with the most reliable:

a. Switch to a POST-Based Confirmation Flow

GET requests are too easy to accidentally trigger. Instead, create a two-step confirmation process: send users a link to a page where they have to click a button to submit a POST request. Automated crawlers almost never submit POST forms, so this eliminates the auto-trigger issue.

Here's a simplified example:

  • When you create the draft post, generate a secure token (more on that below) and send an email with a link like yoursite.com/confirm-submission?post_id=123&token=abc123
  • That link loads a page with a "Publish My Article" button wrapped in a POST form
  • Your publish logic only runs when the form is submitted via POST

Sample code snippets:

// Add rewrite rule for the confirmation page
add_action('init', function() {
    add_rewrite_rule('confirm-submission/?$', 'index.php?confirm_submission=1', 'top');
});

// Register query vars
add_filter('query_vars', function($vars) {
    $vars[] = 'confirm_submission';
    $vars[] = 'post_id';
    $vars[] = 'token';
    return $vars;
});

// Render the confirmation page
add_action('template_redirect', function() {
    if (!get_query_var('confirm_submission')) return;

    $post_id = get_query_var('post_id');
    $token = get_query_var('token');

    // Validate token first (see token generation below)
    if (!validate_confirmation_token($post_id, $token)) {
        wp_die('Invalid or expired confirmation link.');
    }

    // Show the confirmation form
    ?>
    <div style="max-width: 600px; margin: 2rem auto; padding: 2rem;">
        <h1>Confirm Your Submission</h1>
        <p>Click the button below to publish your article.</p>
        <form method="POST" action="<?php echo admin_url('admin-ajax.php'); ?>">
            <?php wp_nonce_field('publish_submission_nonce'); ?>
            <input type="hidden" name="action" value="publish_submission">
            <input type="hidden" name="post_id" value="<?php echo esc_attr($post_id); ?>">
            <input type="hidden" name="token" value="<?php echo esc_attr($token); ?>">
            <button type="submit" style="padding: 0.8rem 1.5rem; background: #0073aa; color: white; border: none; border-radius: 4px; cursor: pointer;">Publish My Article</button>
        </form>
    </div>
    <?php
    exit;
});

// AJAX handler for publish action
add_action('wp_ajax_publish_submission', 'handle_publish_submission');
add_action('wp_ajax_nopriv_publish_submission', 'handle_publish_submission');

function handle_publish_submission() {
    check_ajax_referer('publish_submission_nonce');

    $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
    $token = isset($_POST['token']) ? sanitize_text_field($_POST['token']) : '';

    if (!validate_confirmation_token($post_id, $token)) {
        wp_send_json_error('Invalid request.');
    }

    // Update post status to publish
    wp_update_post([
        'ID' => $post_id,
        'post_status' => 'publish'
    ]);

    wp_send_json_success('Your article has been published!');
}

b. Add Secure, Expiring Tokens

Even if you stick with GET (though POST is better), adding a unique, time-limited token tied to the post will prevent accidental or malicious triggers. Here's how to generate and validate these tokens:

// Generate a token when creating the draft post
function generate_confirmation_token($post_id) {
    $user_id = get_current_user_id();
    $timestamp = time();
    $secret = defined('CONFIRMATION_SECRET') ? CONFIRMATION_SECRET : wp_generate_password(32, false);

    // Create a unique hash from post ID, user ID, timestamp, and a secret
    $token_hash = hash('sha256', $post_id . $user_id . $timestamp . $secret);

    // Store token and 24-hour expiration in post meta
    update_post_meta($post_id, '_confirmation_token', $token_hash);
    update_post_meta($post_id, '_token_expiration', $timestamp + 86400);

    return $token_hash;
}

// Validate the token
function validate_confirmation_token($post_id, $token) {
    $stored_token = get_post_meta($post_id, '_confirmation_token', true);
    $expiration = get_post_meta($post_id, '_token_expiration', true);

    // Check if token exists, matches, and hasn't expired
    if (empty($stored_token) || $stored_token !== $token || time() > $expiration) {
        return false;
    }

    // Invalidate token after use to prevent reuse
    delete_post_meta($post_id, '_confirmation_token');
    delete_post_meta($post_id, '_token_expiration');

    return true;
}

When building your confirmation URL, include the post ID and token:

$token = generate_confirmation_token($draft_post_id);
$confirm_url = home_url("/confirm-submission?post_id={$draft_post_id}&token={$token}");

c. Block Crawler/User Agent Triggers

Add a check in your confirmation handler to ignore requests from common crawlers or email preview tools:

function is_auto_crawler() {
    $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
    $crawlers = ['Googlebot', 'Bingbot', 'FacebookExternalHit', 'LinkedInBot', 'Twitterbot', 'Mail.ru Bot'];

    foreach ($crawlers as $crawler) {
        if (strpos($user_agent, $crawler) !== false) {
            return true;
        }
    }
    return false;
}

// In your confirmation handler:
if (is_auto_crawler()) {
    exit; // Don't process the request
}

3. Double-Check Your wp_mail() Code

Quick sanity check: make sure you're not accidentally calling the publish function when generating the email. For example, if you have a line like wp_update_post(['ID' => $post_id, 'post_status' => 'publish']) near your wp_mail() call, that would immediately publish the post—no confirmation needed.

内容的提问来源于stack exchange,提问作者Aidan Knight

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:38:59