Contact & Enquiry Forms
By the end of this lesson you will add a working enquiry form to your site, protect it from spam bots and make sure the emails actually reach your inbox. You will also see how a developer builds a small secure form in code, which you will reuse in the final project.
A form is the reception desk of your website. A visitor walks in, fills in a slip, and someone in the office must receive it. If the slip gets lost or the desk is flooded with junk, you lose real customers.
Your options
WordPress core does not include a full form builder, so most sites use a plugin. Popular choices in the WordPress.org directory include Contact Form 7, WPForms (free "Lite" version plus paid plans), Fluent Forms and the forms module in Jetpack. All of them let you add a form as a block.
| Approach | Good for | Things to check |
|---|---|---|
| Form plugin | Most sites; no code | Spam protection, whether entries are saved, export |
| Custom code | Developers who want a small, exact form | You must handle security and email yourself |
| Embedded third-party form | Google Forms and similar | Looks less integrated; data stored elsewhere |
Use the two-minute safety check from Choosing Plugins Safely for any form plugin.
Adding a form with a plugin, step by step
The steps are similar in every form plugin. Here is the general flow.
Step 1: Install and activate your chosen form plugin.
Step 2: Open its menu (for example Contact or Forms) and create a new form.
Step 3: Add fields: Name, Phone, Email, Class (a dropdown with 9, 10, 11, 12) and Message. Mark Name and Phone as required.
Step 4: In the form's notification or mail settings, set To as your office email and the Subject as "New enquiry from the website".
Step 5: Save the form. Open your Contact page, add the plugin's form block and choose your form.
Step 6: Publish and submit a test entry. You should see a thank-you message on screen and an email in your inbox within a minute or two.
If the email does not arrive, read the next section.
Making sure emails arrive
WordPress sends email with a function called wp_mail(). By default it uses the server's basic mail system. Many inbox providers distrust these messages, so they land in spam or vanish.
The fix is to send through a proper mail service using SMTP (Simple Mail Transfer Protocol), the standard way mail servers talk. An SMTP plugin, such as WP Mail SMTP or FluentSMTP, connects WordPress to your email provider (your host's mailbox, Google Workspace, Zoho Mail or a transactional email service).
- Step 1: Create a mailbox such as
website@brightfuture.inwith your provider. - Step 2: Install an SMTP plugin and enter the provider's SMTP details or connect with the provider's sign-in option.
- Step 3: Use the plugin's Send test email button.
- Step 4: Ask your domain provider to add the SPF and DKIM DNS records that your mail provider gives you. These prove that your server is allowed to send mail for your domain.
Store SMTP passwords only in the plugin settings or wp-config.php, never inside a page or a shared document.
Stopping spam
Bots fill forms with junk links. Use more than one layer:
- Honeypot: a hidden field that humans never see. Bots fill it, so any entry with it filled is rejected.
- CAPTCHA services such as Cloudflare Turnstile or Google reCAPTCHA, which most form plugins support.
- Akismet or a similar spam filter service.
- Validation: a phone field that accepts only digits stops many junk entries.
Collecting personal data responsibly
An enquiry form collects personal data: names, phone numbers, emails. India's Digital Personal Data Protection Act, 2023 and its rules set duties for anyone who collects such data. Without giving legal advice, good practice is:
- collect only what you need (a coaching enquiry rarely needs a home address)
- say clearly why you collect it, near the form, and link to your Privacy Policy
- do not add people to marketing lists unless they agree
- delete old enquiries you no longer need
Check the current rules or ask a lawyer for anything beyond a simple enquiry form.
A custom form in code
Developers sometimes want a small form with no extra plugin. Here is a secure enquiry form added with a shortcode (a tag like [bf_enquiry_form] that you place in a Shortcode block). You will learn shortcodes properly in Shortcodes vs Blocks.
First, the form. It posts to admin-post.php, WordPress's built-in address for handling form submissions:
<?php
add_shortcode( 'bf_enquiry_form', function () {
if ( isset( $_GET['enquiry'] ) && 'sent' === $_GET['enquiry'] ) {
return '<p>Thank you! Our office will call you within one working day.</p>';
}
ob_start();
?>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<input type="hidden" name="action" value="bf_enquiry">
<?php wp_nonce_field( 'bf_enquiry', 'bf_nonce' ); ?>
<p><label>Name <input type="text" name="bf_name" required></label></p>
<p><label>Phone <input type="tel" name="bf_phone" pattern="[0-9]{10}" required></label></p>
<p class="bf-hp" hidden><label>Leave empty <input type="text" name="bf_website"></label></p>
<p><button type="submit">Send enquiry</button></p>
</form>
<?php
return ob_get_clean();
} );Three security ideas appear here:
wp_nonce_field()adds a nonce, a one-time token that proves the form came from your site.esc_url()makes the address safe to print inside HTML.bf_websiteis the hidden honeypot field.
Next, the handler. WordPress calls admin_post_nopriv_bf_enquiry for logged-out visitors and admin_post_bf_enquiry for logged-in users:
<?php
function bf_handle_enquiry() {
$nonce = isset( $_POST['bf_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['bf_nonce'] ) ) : '';
if ( ! wp_verify_nonce( $nonce, 'bf_enquiry' ) ) {
wp_die( 'Security check failed. Please go back and try again.' );
}
if ( ! empty( $_POST['bf_website'] ) ) {
wp_die( 'Spam detected.' ); // the honeypot was filled
}
$name = sanitize_text_field( wp_unslash( $_POST['bf_name'] ?? '' ) );
$phone = preg_replace( '/\D/', '', wp_unslash( $_POST['bf_phone'] ?? '' ) );
if ( '' === $name || 10 !== strlen( $phone ) ) {
wp_die( 'Please enter your name and a 10-digit phone number.' );
}
wp_mail(
get_option( 'admin_email' ),
'New enquiry from ' . $name,
"Name: $name\nPhone: $phone"
);
$back = wp_get_referer() ?: home_url( '/' );
wp_safe_redirect( add_query_arg( 'enquiry', 'sent', $back ) );
exit;
}
add_action( 'admin_post_nopriv_bf_enquiry', 'bf_handle_enquiry' );
add_action( 'admin_post_bf_enquiry', 'bf_handle_enquiry' );When Rohan submits "Rohan Verma" and "98765 43210", the office receives an email like this:
Subject: New enquiry from Rohan Verma Name: Rohan Verma Phone: 9876543210
Rohan is redirected back to the same page and sees the thank-you message. Notice that the spaces in the phone number were removed by preg_replace(), so "98765 43210" still passes the 10-digit check.
Common mistakes
- Never testing the form after launch. Submit a test entry every month; settings and mail servers change.
- Relying on server mail. Use SMTP and set up SPF and DKIM.
- Sending enquiries to a personal Gmail only. If that person leaves, enquiries go with them. Use an office address.
- Asking for too much data. Every extra field lowers the number of people who finish the form.
- Skipping the nonce and sanitising in custom code.
Key points
- WordPress has no full form builder in core; use a trusted form plugin or careful custom code.
wp_mail()sends mail; use an SMTP plugin plus SPF and DKIM records so emails arrive.- Fight spam with a honeypot, a CAPTCHA service and validation.
- Collect only the personal data you need and explain why, linking to your Privacy Policy.
- Custom forms need a nonce, sanitised input, escaped output and a redirect after success.
Exercise
Extend the custom form with an optional Class dropdown (9, 10, 11, 12). In the handler, accept only those four values; anything else should be saved as "Not given". Add the class to the email body.
Show solution
Add the field to the form:
<p><label>Class
<select name="bf_class">
<option value="">Choose</option>
<option>9</option><option>10</option><option>11</option><option>12</option>
</select>
</label></p>In the handler, check against an allow-list before using it:
<?php
$class = sanitize_text_field( wp_unslash( $_POST['bf_class'] ?? '' ) );
if ( ! in_array( $class, [ '9', '10', '11', '12' ], true ) ) {
$class = 'Not given';
}
$body = "Name: $name\nPhone: $phone\nClass: $class";An allow-list is safer than trying to block bad values, because anything not on the list is rejected.
Quick Quiz
1. Why do WordPress emails often land in spam?
Sending through a real mail service with SPF and DKIM makes messages trustworthy.
2. What is a honeypot field?
Humans never see the hidden field, so a filled honeypot means a bot.
3. Which hook handles a form posted to admin-post.php by a logged-out visitor with action set to bf_enquiry?
The nopriv version runs for visitors who are not logged in.
4. What does wp_nonce_field() add to a form?
The handler checks the token with wp_verify_nonce().
Learn WordPress with a mentor
Free lessons get you started. Our WordPress & Website Building training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
