
Table of Contents
Build a foreign-trade inquiry form that keeps required fields minimal to protect conversion, then block bots with a honeypot plus rate limiting, send through authenticated SMTP for deliverability, and store every lead in the database with instant admin notification. You know it is done right when: legitimate inquiries arrive in your inbox within seconds, spam stays under 1% of submissions, no real lead is ever lost, and you can export or replay any entry from wp_posts or a custom table on demand. This tutorial gives you the exact fields, code, commands and checks to reach that state.
Preparation Before You Touch Code
Start on a staging clone, never production. Use WordPress 6.7 or later (current stable in 2026) with PHP 8.3+. Create a child theme or a small custom plugin so updates do not wipe your work; see Child Theme vs Theme Edits vs Plugin: Code Placement for the decision rule. Install WP Mail SMTP 4.2+ and a security plugin such as Wordfence 8.0+ for firewall-level rate limits. Have SSH access and confirm wp cli 2.11 is available. Back up the database first with wp db export backup-$(date +%F).sql; for restore drills, follow WordPress Site Backup and Restore: Vendor Guide 2026.
Environment Checks
- Run
wp core versionandphp -vto record baseline. - Confirm
opensslextension is loaded:php -m | grep openssl. - Set
WP_DEBUGto true inwp-config.phpon staging only.
Step-by-Step Process to Build the Form
- Define the minimum field set that still qualifies the lead.
- Render the form markup with a hidden honeypot input.
- Enforce server-side rate limiting per IP and per session.
- Configure SMTP with a dedicated sending domain and SPF/DKIM.
- Persist entries to a custom table and trigger admin email.
- Verify delivery, spam ratio and rollback path end to end.
Required Fields vs Conversion
Every extra required field cuts international buyer submissions. For foreign-trade sites we keep only four mandatory inputs: name, company email, product interest (select) and message. Phone, country and quantity are optional; ask them after first reply. A/B data from 2026 shows dropping mandatory phone lifts conversion 18-24% on B2B trade forms.
Implement the field set in a shortcode callback inside your child theme’s functions.php or a class-form.php in your plugin. Mark optional fields clearly with <small>(optional)</small> text so buyers are not scared away.
Get a Quote
Email + requirement + budget range — we reply with a quote within 24 hours.
// Minimal required set - place in child theme functions.php
function ft_inquiry_form_shortcode() {
ob_start();
?>
<form method='post' action='' id='ft-inquiry'>
<label>Name *<input type='text' name='ft_name' required></label>
<label>Email *<input type='email' name='ft_email' required></label>
<label>Interest *<select name='ft_interest' required>
<option value=''>Select</option>
<option value='machinery'>Machinery</option>
<option value='textile'>Textile</option>
</select></label>
<label>Message *<textarea name='ft_msg' required></textarea></label>
<label>Phone <small>(optional)</small><input type='text' name='ft_phone'></label>
<input type='text' name='ft_website' style='display:none' tabindex='-1' autocomplete='off'>
<?php wp_nonce_field('ft_submit','ft_nonce'); ?>
<button type='submit'>Send Inquiry</button>
</form>
<?php
return ob_get_clean();
}
add_shortcode('ft_inquiry','ft_inquiry_form_shortcode');
Verify by submitting with only the four starred fields; it must succeed. Pitfall: browser-native required is client-side only, so always re-validate in PHP. Rollback: comment out the add_shortcode line and the form disappears instantly.
Honeypot and Rate Limiting
The hidden ft_website field above is the honeypot. Humans never see it; bots fill every input. Reject any submission where it is non-empty. Pair this with rate limiting so a single IP cannot hammer the endpoint.
Server-Side Validation
add_action('template_redirect','ft_handle_inquiry');
function ft_handle_inquiry() {
if (!isset($_POST['ft_nonce']) || !wp_verify_nonce($_POST['ft_nonce'],'ft_submit')) return;
if (!empty($_POST['ft_website'])) { wp_die('Spam detected', 403); } // honeypot
$ip = $_SERVER['REMOTE_ADDR'];
$key = 'ft_rl_' . md5($ip);
$count = get_transient($key) ?: 0;
if ($count > 3) { wp_die('Too many requests', 429); }
set_transient($key, $count + 1, 300); // 4 per 5 min
$name = sanitize_text_field($_POST['ft_name']);
$email = sanitize_email($_POST['ft_email']);
$msg = sanitize_textarea_field($_POST['ft_msg']);
if (!$name || !$email || !$msg) { wp_die('Missing required fields', 400); }
// proceed to store and notify
}
Test with curl -X POST -d 'ft_website=http://spam.com&ft_name=x' https://yoursite.com/inquiry; you must get 403. Then POST four times rapidly and the fourth returns 429. Common pitfall: caching plugins may serve the form page from cache and skip PHP; exclude the inquiry page in WP Rocket or set DONOTCACHEPAGE. Rollback: delete the template_redirect action and the transients clear automatically.
SMTP Deliverability
Default wp_mail() uses sendmail and lands in spam for international recipients. Configure authenticated SMTP on a subdomain like mail.yourdomain.com. In WP Mail SMTP 4.2 choose “Other SMTP”, set host smtp.eu.mailgun.org or your provider, port 587, TLS, and the API-stored credentials. Add DNS records: TXT SPF v=spf1 include:mailgun.org ~all, DKIM key from the provider panel, and a DMARC record v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com.
Check with wp mail test you@company.com using the plugin’s built-in CLI-ish tool, or send a real inquiry and inspect headers for spf=pass and dkim=pass. Pitfall: shared hosting blocks outbound 587; open a ticket or use an API-based mailer instead. Rollback: switch the plugin mailer back to “PHP” and remove DNS TXT entries.
Storing and Notifying

Never rely on email alone; store the lead. Create a table on plugin activation with dbDelta.
function ft_create_table() {
global $wpdb;
$charset = $wpdb->get_charset_collate();
$sql = "CREATE TABLE {$wpdb->prefix}ft_inquiries (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name tinytext NOT NULL,
email varchar(100) NOT NULL,
interest varchar(50) NOT NULL,
message text NOT NULL,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
}
register_activation_hook(__FILE__,'ft_create_table');
After validation, insert with $wpdb->insert() then notify via wp_mail() to sales@ and a confirmation to the buyer. Use wp_schedule_single_event if the SMTP call is slow. Verify rows with wp db query "SELECT COUNT(*) FROM wp_ft_inquiries". Pitfall: dbDelta requires exactly two spaces after PRIMARY KEY; otherwise the table is never created. Rollback: DROP TABLE wp_ft_inquiries and deactivate the plugin.
Verification and Rollback Drill
Run the full loop: submit valid data, confirm DB row appears, both emails arrive with passing auth headers, then submit honeypot-filled data and confirm 403. If anything breaks, restore the SQL backup with wp db import backup-2026-01-15.sql and disable the plugin via wp plugin deactivate ft-inquiry. Keep the backup for 30 days.
Action Checklist
- Limit required fields to name, email, interest, message.
- Add hidden honeypot and reject non-empty values.
- Enforce 4-submissions-per-5-minutes per IP.
- Send all mail via authenticated SMTP with SPF/DKIM.
- Store every lead in a custom table before emailing.
Frequently asked questions
How do I know WordPress inquiry form spam prevention is working?
Check that honeypot-filled posts return 403 and your spam folder receives zero legitimate inquiries over a 7-day test window.
Can I use a page builder form instead of custom code?
Yes, but map the same four required fields and enable the builder’s honeypot plus SMTP; see our Custom WordPress Development service for hardened builds.
What if SMTP still fails after DNS setup?
Verify port 587 is open with telnet smtp.provider.com 587; if blocked, use an HTTPS API mailer instead of raw SMTP.
Need a hardened trade inquiry system? Our team builds and maintains it.
