Email Contact Form – Mailtrap https://mailtrap.io Modern email delivery for developers and product teams Wed, 08 Oct 2025 14:08:23 +0000 en-US hourly 1 https://mailtrap.io/wp-content/uploads/2023/01/cropped-favicon-1-32x32.png Email Contact Form – Mailtrap https://mailtrap.io 32 32 Django Contact Form https://mailtrap.io/blog/django-contact-form/ Fri, 26 Jul 2024 15:22:51 +0000 https://mailtrap.io/?p=16682 Python web framework Django is great for rapid and secure web development, providing an easy way to create and customize a contact form.

Whether you’re a beginner or a Jedi developer who needs a refresher, this tutorial provides all the steps to building a robust contact form that can be featured on your homepage. And I’ll cover:

Ready to deliver your emails?
Try Mailtrap for Free

How to create a Django contact form

Make sure that Django is installed on your machine. From the command prompt, run this command:

pip install django

If you have a particular version that you need, and not the latest one that is installed by default, run this command and indicate the version number:

pip install django==4.2

Next, you’ll need to set up an actual project before beginning with the contact form. 

Do this by running the command django-admin startproject <projectname>, where <projectname> is the name you want to give to your Django project. 

In this example, I’ll name it myproject

django-admin startproject myproject

The command creates a new directory called myproject that contains the main settings and configuration files.

myproject/
    manage.py
    myproject/
        __init__.py
        settings.py
        urls.py
        asgi.py
        wsgi.py

***The manage.py file is a command-line utility that helps to interact with the project.

Now it’s time to build a basic contact form.

Start by creating a new forms.py file in the myproject folder.

To access various form-related functionalities, such as validation and rendering, create a form class that inherits Django’s built-in Form class. 

This is where you can define the form fields you want to collect from the user. Here’s the full code that will go in the file:

from django import forms


class ContactForm(forms.Form):
    name = forms.CharField(required=True)
    email = forms.EmailField(required=True)
    message = forms.CharField(widget=forms.Textarea)

This is just a basic example, but you can add additional fields and built-in validations to ensure that the user inputs the correct data:

from django import forms
from django.core.validators import EmailValidator


class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.CharField(validators=[EmailValidator()])
    phone = forms.CharField(max_length=15)
    subject = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea)

Once the contact form class is created, create a new views.py file, and in it, add a view function that’ll render the contact form template and handle the submission:

from django.shortcuts import render, redirect
from myproject.forms import ContactForm
from django.http import HttpResponse


def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            # Process the form data
            pass
            return redirect('success')
    else:
        form = ContactForm()
    return render(request, 'contact.html', {'form': form})


def success(request):
  return HttpResponse('Success!')

Here, I defined a view function called contact that takes a web request and returns a rendered contact form template.

The view function is responsible for:

  • Processing the form data submitted by the user
  • Checking if the request is a POST request 
  • Creating an instance of the ContactForm class to validate the submitted data via is_valid() method

In this example, we simply ‘pass’, but you can also:

  1. Send form data via email. We’ll cover this in more detail with code examples in the next step below. 
  1. Save form data to a database:
import csv
from django.shortcuts import redirect

name = form.cleaned_data['name']
email = form.cleaned_data['email']
message = form.cleaned_data['message']

file = open('responses.csv', 'a')
writer = csv.writer(file)
writer.writerow([name,email,message])
file.close()

return redirect('success')

Note that this approach saves the form data to a CSV file instead of a database. To save form data to a database, you’ll need to define a model to store the data. 

For more information on defining models in Django, check out the Django tutorial on setting up models. But, I’ll also show you another method to collect data in the section below (hit the link to jump to that). 

To use the view function I created, I’ll map a URL to the view function to create a webpage or endpoint accessible via the URL.

This is done using the path() function that defines a URL pattern that points to the contact() function. Note that this code should go to the file called myproject/urls.py

from django.urls import path
from myproject.views import contact, success

urlpatterns = [
    path('contact/', contact, name='contact'),
    path('success/', success, name='success')
]

At this point, I’m moving on to write the HTML for the contact form from scratch or use a ready-made template file. There are several sources for HTML templates, and the one you choose depends on the specific design and functionality you need.

Some of the sources can either be:

Here are some simple steps you can take to set up a template that works with our guide:

  • Add this to TEMPLATES setting in settings.py:
'DIRS': [
          os.path.join(BASE_DIR, "templates")
      ],
  • Create templates/contact.html in our project with the following contents:
<form action="/contact/" method="post">
  {% csrf_token %}
  {% for field in form %}
      <div class="fieldWrapper">
          {{ field.errors }}
          {{ field.label_tag }} {{ field }}
      </div>
  {% endfor %}
  <input type="submit" value="Submit">
</form>
  • Create templates/success.html in our project with the following contents:
<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Success Page</title>

    <style>

        body {

            font-family: Arial, sans-serif;

            display: flex;

            justify-content: center;

            align-items: center;

            height: 100vh;

            margin: 0;

            background-color: #f4f4f4;

        }

        .success-message {

            text-align: center;

            padding: 20px;

            border: 2px solid #4caf50;

            border-radius: 5px;

            background-color: #dff0d8;

            color: #3c763d;

        }

    </style>

</head>

<body>

    <div class="success-message">

        <h1>Success!</h1>

        <p>Your operation was completed successfully.</p>

    </div>

</body>

</html>
  • As a last touch, add a success page to which the user is redirected to after hitting the submit button:
from django.shortcuts import render, redirect
from myproject.forms import ContactForm

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            # Process the form data
            return redirect('success')
    else:
        form = ContactForm()
    return render(request, 'contact.html', {'form': form})

def success(request):
    return render(request, 'success.html')

To run all of the example codes from this tutorial, and to check the result, execute the following command in your terminal:

python3 manage.py runserver

After that, open your web browser and enter this URL http://127.0.0.1:8000/contact/ to access the contact form page. Here’s an example of what you should see: 

How to add reCaptcha

To make sure humans fill out the form, I’ll show you how to add reCaptcha to the form above. This includes registering for the service, updating all dependencies, and testing the setup. 

Step 1: Register for reCAPTCHA

  • Go to the Google reCAPTCHA website.
  • Sign in with your Google account.
  • Register a new site by entering your label and selecting reCAPTCHA v2 with the “I’m not a robot” checkbox.
  • Add your domain to the list of domains authorized to use your reCAPTCHA keys.
  • Upon registration, you’ll receive two key pieces of data: a Site key and a Secret key. Keep these handy as you’ll need them in the next steps.

Step 2: Update Django settings

Add the reCAPTCHA keys to your Django settings:

# settings.py

RECAPTCHA_PUBLIC_KEY = 'your_site_key'
RECAPTCHA_PRIVATE_KEY = 'your_secret_key'

Step 3: Install Django reCAPTCHA

Install the django-recaptcha library, which provides Django integrations for reCAPTCHA:

pip install django-recaptcha

Add it to the installed apps under the Django settings:

# settings.py

INSTALLED_APPS = [
    ...
    'django_recaptcha',
]

Step 4: Update the form

Modify the ContactForm to include the reCAPTCHA field:

# forms.py
from django import forms
from django_recaptcha.fields import ReCaptchaField
from django_recaptcha.widgets import ReCaptchaV2Checkbox

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.CharField(max_length=100)
    subject = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea)
    captcha = ReCaptchaField(widget=ReCaptchaV2Checkbox())

Step 5: Update the Template

Modify the contact.html template to ensure the form template includes the reCAPTCHA widget.

<form action="/contact/" method="post">
    {% csrf_token %}
    {% for field in form %}
        <div class="fieldWrapper">
            {{ field.errors }}
            {{ field.label_tag }} {{ field }}
        </div>
    {% endfor %}
    <input type="submit" value="Submit">
</form>

Note: Django reCAPTCHA automatically includes the necessary JavaScript and widget code, so there’s no need for additional script tags.

Step 6: Update the view

Ensure your view handles the reCAPTCHA validation:

# views.py
from django.shortcuts import render, redirect
from .forms import ContactForm

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            # Process the form data
            return redirect('success')
    else:
        form = ContactForm()

    return render(request, 'contact.html', {'form': form})

Step 7: Testing

Run your Django development server and navigate to the contact form page. You should see the reCAPTCHA checkbox below your form fields. Test it by filling out your form and verifying that the reCAPTCHA validates inputs as expected.

How to collect data from a Django contact form

First, check if the contact form in forms.py is set up to collect all the data you need. Here’s a quick review of the form I previously made:

# forms.py
from django import forms
from django.core.validators import EmailValidator

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField(validators=[EmailValidator()])
    phone = forms.CharField(max_length=15, required=False)  # Optional field
    subject = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea)

Next, in the views.py, I’ll add a function to handle the POST request when the form is submitted. This function will validate the form data that then can be processed (e.g., by sending an email or saving it).

# views.py
from django.shortcuts import render, redirect
from forms import ContactForm
from django.core.mail import send_mail
from django.conf import settings

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            name = form.cleaned_data['name']
            email = form.cleaned_data['email']
            phone = form.cleaned_data['phone']
            subject = form.cleaned_data['subject']
            message = form.cleaned_data['message']

            # Send an email
            send_mail(
                f'From {name}, Subject: {subject}',
                f'Message: {message}\nContact Phone: {phone}',
                email,  # From email
                [settings.ADMIN_EMAIL],  # To email
                fail_silently=False,
            )

            return redirect('success')
    else:
        form = ContactForm()

    return render(request, 'contact.html', {'form': form})

Quick breakdown:

  • form.cleaned_data is used to access validated data from your form.
  • send_mail is a Django utility to send emails. Note: you need to set up your email backend settings in settings.py to use this feature.

Now, to send emails, you need to configure the email backend in your Django project settings. 

Important Note: The code here is just an exemplary setting, I’ll show you the proper sending method with Mailtrap Email API/SMTP in the following sections. Hit the links to jump to these methods.  

# settings.py

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'live.smtp.mailtrap.io'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your-email@yourserver.com'
EMAIL_HOST_PASSWORD = 'your-email-password'
ADMIN_EMAIL = 'admin@yourdomain.com'

With that, I’ll move on to create a success page. You need to have a success page to redirect to after the form is processed, here’s the code:

# views.py

def success(request):
    return render(request, 'success.html', {'message': 'Thank you for contacting us!'})

Then, I’ll create a simple success.html template:

<!-- templates/success.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Success</title>
</head>
<body>
    <p>{{ message }}</p>
</body>
</html>

Lastly, you should test out the form. Do the following:

  • Fill out the form on your website and submit it.
  • Check if the email is received.
  • Verify that the data is correctly collected and displayed.

How to validate and verify data from a contact form

Django provides a robust system for form validation that you can utilize to enforce various constraints and checks. Below, I’ll guide you through enhancing your existing form validation, including server-side and custom validation techniques.

The framework comes with a set of built-in validators that you can use directly in your form fields to enforce specific data rules such as: 

  • Length constraints 
  • Email format 
  • Regex patterns 
  • etc.

Here’s how you can enhance the ContactForm in your forms.py to include more detailed built-in validators:

# forms.py
from django import forms
from django.core.validators import EmailValidator, RegexValidator, MinLengthValidator

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100, validators=[MinLengthValidator(2)])
    email = forms.EmailField(validators=[EmailValidator(message="Enter a valid email address.")])
    phone = forms.CharField(max_length=15, validators=[
        RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
    ], required=False)  # Optional field
    subject = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea, validators=[MinLengthValidator(10)])

For more complex validation scenarios that go beyond what is provided by Django’s built-in validators, you can define custom validation methods within your form class. These methods should start with clean_ followed by the field name.

For instance, let’s add a custom validation to ensure that the message does not contain any unwanted characters or words:

The clean_message method is automatically called by Django’s form validation system when you call the is_valid method on a form instance. You generally don’t need to call it explicitly.

# forms.py
def clean_message(self):
    data = self.cleaned_data['message']
    unwanted_words = ['spam', 'money', 'subscribe']
    if any(word in data.lower() for word in unwanted_words):
        raise forms.ValidationError("Please do not include spam words in your message.")
    return data

Also, sometimes there’s a need to validate fields in combination (e.g., checking that one field’s value is greater than another). Django allows you to override the clean() method of the form class for such purposes:

# forms.py
def clean(self):
    cleaned_data = super().clean()
    subject = cleaned_data.get('subject')
    message = cleaned_data.get('message')
    if 'help' in subject.lower() and 'urgent' not in message.lower():
        raise forms.ValidationError("Urgent help requests should include 'urgent' in the message.")

Lastly, it’s important to update the view to make sure it handles these validations correctly and provides feedback to the user when errors occur. Check the snippet below:

# views.py
from django.shortcuts import render, redirect
from forms import ContactForm

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            # process the form data
            return redirect('success')
        else:
            # Return to the form page with validation errors
            return render(request, 'contact.html', {'form': form})
    else:
        form = ContactForm()

    return render(request, 'contact.html', {'form': form})

And yes, don’t forget to test the form with validators before you deploy it to production. It’s best to try to fail the validation and then check the error messages to see if you need to adjust the HTML code. 

Send an email from a Django contact form using SMTP

There are a few actions that you can set the form to do once it’s submitted. In most cases, you’ll want it to send an email with the form’s information. 

As shown earlier, you’ll need to list the SMTP server configuration and your credentials in the settings.py file.

In the following code example, I’ll use Mailtrap Email API/SMTP – the SMTP method. And later I’ll show you how to do the same with Mailtrap API. 

Note: This can also be done with Gmail or any other SMTP server of an email service provider.

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'live.smtp.mailtrap.io'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'example_username'
EMAIL_HOST_PASSWORD = 'example_password'
EMAIL_USE_TLS = True

Once you’ve configured your email settings, use Django’s built-in email sending functionality to send an email in your view function.

Here’s a full code of how to modify the contact() view function we created earlier to send an email:

from django.shortcuts import render, redirect
from django.core.mail import EmailMessage
from myproject.forms import ContactForm
from django.conf import settings

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            name = form.cleaned_data['name']
            email = form.cleaned_data['email']
            message = form.cleaned_data['message']


          EmailMessage(
              'Contact Form Submission from {}'.format(name),
              message,
              'form-response@example.com', # Send from (your website)
              ['JohnDoe@gmail.com'], # Send to (your admin email)
              [],
              reply_to=[email] # Email from the form to get back to
          ).send()

            return redirect('success')
    else:
        form = ContactForm()
    return render(request, 'contact.html', {'form': form})

Send an email from a Django contact form using API

Mailtrap API/SMTP has an official Python client, so start by installing that. 

pip install mailtrap

Proceed to create a mail object, making sure you have the proper:

  • Sender 
  • Receiver 
  • Subject
  • Text

I’m stressing this because the code below is for exemplary purposes. And one more thing – when using Mailtrap your sender address needs to match the domain you added and verified on the platform. 

import mailtrap as mt

# create mail object
mail = mt.Mail(
    sender=mt.Address(email="mailtrap@example.com", name="Mailtrap Test"),
    to=[mt.Address(email="your@email.com")],
    subject="You are awesome!",
    text="Congrats for sending test email with Mailtrap!",
)

Then, you need a short snippet that creates the client and sends the email, here it is: 

client = mt.MailtrapClient(token="your-api-key")
client.send(mail)

Tip: You can find your Mailtrap API key under Settings > API Tokens

That’s pretty much it, you can now send a simple email. But if you need a more complex message with attachments, multiple recipients, and categories, here’s the giga snippet. 

Create a sendmail.py file in the same root directory of your project. And write the following code snippet there.

import base64
from pathlib import Path

import mailtrap as mt

welcome_image = Path(__file__).parent.joinpath("welcome.png").read_bytes()

mail = mt.Mail(
    sender=mt.Address(email="mailtrap@example.com", name="Mailtrap Test"),
    to=[mt.Address(email="your@email.com", name="Your name")],
    cc=[mt.Address(email="cc@email.com", name="Copy to")],
    bcc=[mt.Address(email="bcc@email.com", name="Hidden Recipient")],
    subject="You are awesome!",
    text="Congrats for sending test email with Mailtrap!",
    html="""
    <!doctype html>
    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      </head>
      <body style="font-family: sans-serif;">
        <div style="display: block; margin: auto; max-width: 600px;" class="main">
          <h1 style="font-size: 18px; font-weight: bold; margin-top: 20px">
            Congrats for sending test email with Mailtrap!
          </h1>
          <p>Inspect it using the tabs you see above and learn how this email can be improved.</p>
          <img alt="Inspect with Tabs" src="cid:welcome.png" style="width: 100%;">
          <p>Now send your email using our fake SMTP server and integration of your choice!</p>
          <p>Good luck! Hope it works.</p>
        </div>
        <!-- Example of invalid for email html/css, will be detected by Mailtrap: -->
        <style>
          .main { background-color: white; }
          a:hover { border-left-width: 1em; min-height: 2em; }
        </style>
      </body>
    </html>
    """,
    category="Test",
    attachments=[
        mt.Attachment(
            content=base64.b64encode(welcome_image),
            filename="welcome.png",
            disposition=mt.Disposition.INLINE,
            mimetype="image/png",
            content_id="welcome.png",
        )
    ],
    headers={"X-MT-Header": "Custom header"},
    custom_variables={"year": 2023},
)

def sendEmail():
    client = mt.MailtrapClient(token="your-api-key")
    client.send(mail)

In order to call this sendEmail function you would need to import this function within your API handler that receives form data and call like this.

```python
from sendmail import sendEmail
# .....previous code
def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            name = form.cleaned_data['name']
            email = form.cleaned_data['email']
            phone = form.cleaned_data['phone']
            subject = form.cleaned_data['subject']
            message = form.cleaned_data['message']

            sendEmail()
.........

For more info on the sending methodology, check our detailed article on sending with Django, or go to the official Python SDK documentation

Wrapping up

With these steps, you’ll be able to develop a reliable and efficient contact form for your website that’ll help you to connect with your users. So go ahead and implement a Django contact form on your website today! And if you need more helpful topics about Django and Python, check these out: 

]]>
PHP Email Contact Form https://mailtrap.io/blog/php-email-contact-form/ https://mailtrap.io/blog/php-email-contact-form/#comments Thu, 25 Jul 2024 15:58:08 +0000 https://blog.mailtrap.io/?p=4257 Email contact forms are the bread and butter for any campaign to collect leads, customers, or hook potential users into your funnel. 

The cool thing is that they’re relatively easy to embed into your webpage, even for beginners. Yet, email harvesters will struggle to abuse them when set up correctly. 

Here, I’ll be showing you how to build an effective contact form in PHP. I’m going to explain the process step by step in this article, covering the following.

And before we begin, I’ll also talk about troubleshooting sending issues. So, let’s roll up our sleeves.

Ready to deliver your emails?
Try Mailtrap for Free

How to build a PHP contact form

For starters, I’ll build a simple form with just HTML code. If you don’t care much about the visual side of it, it can be as simple as this:

<form>
<h2>Contact us</h2>
<p><label>First Name:</label> <input name="myEmail" type="text" /></p>
<p><label>Email Address:</label> <input style="cursor: pointer;" name="myEmail" type="text" /></p>
<p><label>Message:</label>  <textarea name="message"></textarea> </p>
<p><input type="submit" value="Send" /></p>
</form>

Of course, without any CSS it looks really ugly (no div class or anything). But it will be just fine for demonstration purposes, and I want to focus on performance and security rather than aesthetics. 

Pro Tip: If you’re not into writing CSS at the moment, you can use any of the hundreds of available form builders. Some options include Simfatic, 123FormBuilder, and PHP Jabbers. CodeCanyon has hundreds of tools with reviews for each to make the choice easier.  

Add the “Accept terms/privacy policy” checkbox

Adding the checkbox ensures users must agree to the terms and privacy policies before submitting the form, enhancing both usability and compliance. Here’s how to do it:

<form>
    <h2>Contact us</h2>
    <p><label>First Name:</label> <input name="firstName" type="text" /></p>
    <p><label>Email Address:</label> <input style="cursor: pointer;" name="email" type="email" /></p>
    <p><label>Message:</label> <textarea name="message"></textarea></p>
   
    <!-- Checkbox for accepting terms/privacy policy -->
    <p>
        <input type="checkbox" id="terms" name="terms" required>
        <label for="terms">I accept the <a href="terms.html">Terms of Service</a> and <a href="privacy.html">Privacy Policy</a>.</label>
    </p>

    <p><input type="submit" value="Send" /></p>
</form>

Quick breakdown

  • Email input type: I’ve changed the type of the email address input to type="email" to enforce a valid email format.
  • Name attributes: I updated the name attributes for the first name and email fields to be distinct (firstName and email) to correctly identify the fields in the PHP backend.
  • Checkbox for Terms and Conditions: I added a checkbox for accepting the terms and privacy policy, which is required before the form can be submitted.
  • Links to documents: I included links to “Terms of Service” and “Privacy Policy” pages within the checkbox label for user accessibility.

Add Google reCaptcha

To add an additional security layer, you may want to add a simple reCaptcha script to your PHP mail form. 

First, head to https://www.google.com/recaptcha/admin/create and fill out the form. Next, choose the reCaptcha version that you’d like to use (make sure it’s supported). I’ll opt for v2 for this tutorial. 

ReCaptcha v2 is sent with a form, and you can easily handle it in the backend with the rest of the form fields. ReCaptcha v3, on the other hand, needs to be called manually on the frontend. This is doable but would require us to rewrite the entire code. Let’s leave this for another occasion.

Submit the form and you’ll see your individual Site Key and Secret Key. Check out the whole form with the added ReCaptcha:

<?php

$errors = [];
$errorMessage = '';

$secret = 'your secret key';

if (!empty($_POST)) {
    $name = $_POST['name'];
    $email = $_POST['email'];
    $message = $_POST['message'];
    $recaptchaResponse = $_POST['g-recaptcha-response'];

    $recaptchaUrl = "https://www.google.com/recaptcha/api/siteverify?secret={$secret}&response={$recaptchaResponse}";
    $verify = json_decode(file_get_contents($recaptchaUrl));

    if (!$verify->success) {
      $errors[] = 'Recaptcha failed';
    }

    if (empty($name)) {
        $errors[] = 'Name is empty';
    }

    if (empty($email)) {
        $errors[] = 'Email is empty';
    } else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = 'Email is invalid';
    }

    if (empty($message)) {
        $errors[] = 'Message is empty';
    }


    if (!empty($errors)) {
        $allErrors = join('<br/>', $errors);
        $errorMessage = "<p style='color: red;'>{$allErrors}</p>";
    } else {
        $toEmail = 'some.nugget@gmail.com';
        $emailSubject = 'New email from your contant form';
        $headers = ['From' => $email, 'Reply-To' => $email, 'Content-type' => 'text/html; charset=iso-8859-1'];

        $bodyParagraphs = ["Name: {$name}", "Email: {$email}", "Message:", $message];
        $body = join(PHP_EOL, $bodyParagraphs);

        if (mail($toEmail, $emailSubject, $body, $headers)) {
            header('Location: thank-you.html');
        } else {
            $errorMessage = "<p style='color: red;'>Oops, something went wrong. Please try again later</p>";
        }
    }
}

?>

<html>
<body>
  <script src="https://www.google.com/recaptcha/api.js"></script>
  <form action="/form.php" method="post" id="contact-form">
    <h2>Contact us</h2>

    <?php echo((!empty($errorMessage)) ? $errorMessage : '') ?>
    <p>
      <label>First Name:</label>
      <input name="name" type="text"/>
    </p>
    <p>
      <label>Email Address:</label>
      <input style="cursor: pointer;" name="email" type="text"/>
    </p>
    <p>
      <label>Message:</label>
      <textarea name="message"></textarea>
    </p>

    <p>
      <button
        class="g-recaptcha"
        type="submit"
        data-sitekey="your site key"
        data-callback='onRecaptchaSuccess'
      >
        Submit
      </button>
    </p>
  </form>
  <script src="//cdnjs.cloudflare.com/ajax/libs/validate.js/0.13.1/validate.min.js"></script>
  <script>
      const constraints = {
          name: {
              presence: {allowEmpty: false}
          },
          email: {
              presence: {allowEmpty: false},
              email: true
          },
          message: {
              presence: {allowEmpty: false}
          }
      };

      const form = document.getElementById('contact-form');

      form.addEventListener('submit', function (event) {
          const formValues = {
              name: form.elements.name.value,
              email: form.elements.email.value,
              message: form.elements.message.value
          };

          const errors = validate(formValues, constraints);

          if (errors) {
              event.preventDefault();
              const errorMessage = Object
                  .values(errors)
                  .map(function (fieldValues) {
                      return fieldValues.join(', ')
                  })
                  .join("\n");

              alert(errorMessage);
          }
      }, false);

      function onRecaptchaSuccess () {
          document.getElementById('contact-form').submit()
      }
  </script>
</body>
</html>

How to collect data from a PHP contact form

Okay, the contact form is in place, but whatever data users insert goes straight into a black hole. So, I’ll need to add two more elements to the form – ACTION, and METHOD. 

  1. ACTION tells the browser what it should do when a user hits the Send button.
  2. METHOD tells it how to approach this move.

In this case, I want to load a new PHP page in the background. The form will be processing the data, so you’re legally obliged to protect the user’s details (name and email address), making the POST method the safer option. 

To explain, using GET would mean these details get included in the URL of the following page, something I’d rather avoid.

Now, all that needs to be done is to include these two attributes in the code we previously used:

<form method="POST" action="form.php" id="contact-form">
<h2>Contact us</h2>
<p><label>First Name:</label> <input name="name" type="text" /></p>
<p><label>Email Address:</label> <input style="cursor: pointer;" name="email" type="text" /></p>
<p><label>Message:</label>  <textarea name="message"></textarea> </p>
<p><input type="submit" value="Send" /></p>
</form>

How to validate and verify data from a contact form

To get rid of potential spammers and protect your users from accidentally mistyping their contact details, it’s worth adding some validation and verification algorithms to the contact form. 

For top-notch security, consider doing this on the client- and the server side. 

Client-side validation will quickly return any errors on the frontend, letting a user fix them right away. Server-side validation will also catch those that passed the initial test (by, for example, disabling JavaScript in the browser) but shouldn’t have.

Note that you can write your own script, but it’s often worth using what’s already been built and tested. These scripts also do verification and offer a much better experience than, for example, a regex. 

I’ll use a bulletproof solution for schema validation – https://validatejs.org/. For simplicity, just add a library from a CDN.

<script src="//cdnjs.cloudflare.com/ajax/libs/validate.js/0.13.1/validate.min.js"></script>

Here’s the script.

<script>
   const constraints = {
       name: {
           presence: { allowEmpty: false }
       },
       email: {
           presence: { allowEmpty: false },
           email: true
       },
       message: {
           presence: { allowEmpty: false }
       }
   };

   const form = document.getElementById('contact-form');

   form.addEventListener('submit', function (event) {
     const formValues = {
         name: form.elements.name.value,
         email: form.elements.email.value,
         message: form.elements.message.value
     };

     const errors = validate(formValues, constraints);

     if (errors) {
       event.preventDefault();
       const errorMessage = Object
           .values(errors)
           .map(function (fieldValues) { return fieldValues.join(', ')})
           .join("\n");

       alert(errorMessage);
     }
   }, false);
</script>

For the server-side validation, you can use the following code:

<?php

$errors = [];

if (!empty($_POST)) {
   $name = $_POST['name'];
   $email = $_POST['email'];
   $message = $_POST['message'];
  
   if (empty($name)) {
       $errors[] = 'Name is empty';
   }

   if (empty($email)) {
       $errors[] = 'Email is empty';
   } else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
       $errors[] = 'Email is invalid';
   }

   if (empty($message)) {
       $errors[] = 'Message is empty';
   }
}

If either verification fails, it would be a good idea to let the user know. You can use the following code to build an error message:

<?php

if (!empty($errors)) {

   $allErrors = join('<br/>', $errors);

   $errorMessage = "<p style='color: red;'>{$allErrors}</p>";

}

You are free to render this message anywhere on your page. 

Set up PHPMailer to send emails from a PHP contact form

PHPMailer is a great choice for sending emails from a PHP contact form due to its ease of use, advanced features, and robust error handling. 

It offers compatibility across various email servers, has strong community support, and enhances email security through SMTP authentication. 

However, keep in mind that PHPMailer doesn’t have any built-in email sending. Therefore, I’ll be covering the initial installation and SMTP setup, then I’ll move to show you how to send emails via SMTP and API. 

Installation

To integrate PHPMailer into your project, the first step is to get Composer; it’s a tool for managing dependencies in PHP projects. 

After setting up Composer, you should update your composer.json file by adding the following line:

"phpmailer/phpmailer": "^6.9"

Alternatively, you can directly execute this command in your terminal:

composer require phpmailer/phpmailer

Important Note: Remember, the vendor directory and the vendor/autoload.php script are created by Composer and are not part of PHPMailer itself.

PHPMailer can also be installed manually. Download the PHPMailer source files and add them in your project’s include path as defined in the PHP configuration. Then, you can manually load the necessary class files:

<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require 'path/to/PHPMailer/src/Exception.php';
require 'path/to/PHPMailer/src/PHPMailer.php';
require 'path/to/PHPMailer/src/SMTP.php';

Pro Tip: Incorporate the Exception class is to handle and debug errors effectively. Without it, you might encounter a generic error message indicating the absence of the Exception class without detailed debugging information.

To minimize the installation footprint, you can install PHPMailer with only the essential files. 

  • SMTP functionality – include src/PHPMailer.php and src/SMTP.php
  • XOAUTH2 – also include src/OAuth.php along with any dependencies required for authentication

SMTP settings

An external SMTP server to send emails via PHPMailer enhances reliability and security. When configured with TLS or SSL encryption, it ensures that your PHP application’s email transmissions are secure.

Check the basic setup for SMTP settings using PHPMailer:

<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;

require 'vendor/autoload.php'; // Adjust the path as needed if you're not using Composer

$mail = new PHPMailer(true);

try {
    //Server settings
    $mail->isSMTP();                                            // Send using SMTP
    $mail->Host       = 'smtp.example.com';                     // Set the SMTP server to send through
    $mail->SMTPAuth   = true;                                   // Enable SMTP authentication
    $mail->Username   = 'your_email@example.com';               // SMTP username
    $mail->Password   = 'your_password';                        // SMTP password
    $mail->SMTPSecure = 'tls';                                  // Enable TLS encryption
    $mail->Port       = 587;                                    // TCP port to connect to

The code here includes only placeholder values in the script for exemplary purposes. I’ll be changing that to Mailtrap API/SMTP credentials and server details in the following sections. Production SMTP setup for PHP on a VPS involves additional security and server optimization considerations beyond these settings.

Send email from a PHP contact form using SMTP

As mentioned, I’ll be using Mailtrap API/SMTP to send the email. It’s part of Mailtrap Email Delivery Platform, an email-sending solution for developer and product teams, focused on fast delivery and high inboxing rates for transactional and promo emails.

Now, assuming you’ve added and verified your sending domain, it’s time to move onto the SMTP settings. Since I’m sending from an email contact form, I’ll use Mailtrap’s Transactional Stream to send the confirmation to the recipient. 

Mailtrap sending streams transactional and bulk

Please make sure to copy-paste the transactional stream credentials into your scrip, and the final result may look something like the below. 

<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require 'vendor/autoload.php'; // Adjust based on your installation method

$mail = new PHPMailer(true); // Enable exceptions

// SMTP Configuration
$mail->isSMTP();
$mail->Host = 'live.smtp.mailtrap.io'; // Your SMTP server
$mail->SMTPAuth = true;
$mail->Username = 'your_username'; // Your Mailtrap username
$mail->Password = 'your_password'; // Your Mailtrap password
$mail->SMTPSecure = 'tls';
$mail->Port = 587;

// Sender and recipient settings
$mail->setFrom('from@example.com', 'From Name');
$mail->addAddress('recipient@example.com', 'Recipient Name');

// Sending plain text email
$mail->isHTML(false); // Set email format to plain text
$mail->Subject = 'Your Subject Here';
$mail->Body    = 'This is the plain text message body';

// Send the email
if(!$mail->send()){
    echo 'Message could not be sent. Mailer Error: ' . $mail->ErrorInfo;
} else {
    echo 'Message has been sent';
}

To start using Mailtrap yourself, hit the button below. 

Send email with attachment

Depending on the type of contact form you’re using, there are cases where you may consider adding an attachment to an email from the contact form. For instance, this sometimes applies to:

Anyway, there are two ways to include attachments with PHPMailer and I’ll be covering both below. Just for the fun of it, I’ll assume the file is an event ticket. 

Attach files directly from a filesystem

To be efficient, store these files in the same directory as your PHP script. Then, simply reference its path in the script. Optionally, you can redefine the filename when attaching it:

$mail->addAttachment('path/to/eventticket1.pdf', 'eventticket1.pdf');

In this command, the file at path/to/eventticket1.pdf is attached to your email. The eventticket1.pdf parameter is optional and lets you specify an alternate filename for the recipient. This method isn’t limited to PDFs, you can attach images and other file types similarly.

If you want to attach multiple files, repeat the attachment line with new paths and filenames:

$mail->addAttachment('path/to/eventticket2.pdf', 'eventticket2.pdf');

Incorporate string attachments

PHPMailer also allows you to attach data strings directly. This is particularly useful for sending data that doesn’t need to be saved as a file on your server, such as extracting binary data from a database.

For attaching data from a database like a BLOB, you might use:

$mysql_data = $mysql_row['blob_data'];
$mail->addStringAttachment($mysql_data, 'db_data.db');

This command attaches data directly from a MySQL database BLOB field.

Lastly, it’s possible to attach files from external sources by fetching the contents via URL:

$mail->addStringAttachment(file_get_contents($url), 'myfile.pdf');

This technique allows you to dynamically include content from external sources directly into your emails without downloading and saving them first.

Send email from a PHP contact form using email API

If you need to process thousands of contact submissions and send equal emails, I’d always recommend using email API. Why? It offers additional flexibility, automations, and could be easier to integrate with your existing systems.

Again, I’ll use Mailtrap API/SMTP – its official PHP SDK. But, before I start, here are some considerations to kept in mind. 

  • The official package can be installed with Composer. 
  • This client isn’t hard-coupled with Symfony HTTP, Guzzle, Zend, React or other libraries to send HTTP requests. 
  • The client uses PSR-18 client abstraction to have the flexibility to choose your own client. 

If you want a quick start, run the following commands. 

Symfony HTTP client (recommended)

composer require railsware/mailtrap-php symfony/http-client nyholm/psr7

Guzzle HTTP client

composer require railsware/mailtrap-php guzzlehttp/guzzle php-http/guzzle7-adapter

With that out of the way, check how to send an email via Mailtrap PHP SDK. 

<?php

use Mailtrap\Config;
use Mailtrap\EmailHeader\CategoryHeader;
use Mailtrap\EmailHeader\CustomVariableHeader;
use Mailtrap\Helper\ResponseHelper;
use Mailtrap\MailtrapClient;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\UnstructuredHeader;

require __DIR__ . '/vendor/autoload.php';

// your API token from here https://mailtrap.io/api-tokens
$apiKey = getenv('MAILTRAP_API_KEY');
$mailtrap = new MailtrapClient(new Config($apiKey));

$email = (new Email())
    ->from(new Address('example@your-domain-here.com', 'Mailtrap Test'))
    ->replyTo(new Address('reply@your-domain-here.com'))
    ->to(new Address('email@example.com', 'Jon'))
    ->priority(Email::PRIORITY_HIGH)
    ->cc('mailtrapqa@example.com')
    ->addCc('staging@example.com')
    ->bcc('mailtrapdev@example.com')
    ->subject('Best practices of building HTML emails')
    ->text('Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap's Guide on How to Build HTML Email is live on our blog')
    ->html(
        '<html>
        <body>
        <p><br>Hey</br>
        Learn the best practices of building HTML emails and play with ready-to-go templates.</p>
        <p><a href="https://mailtrap.io/blog/build-html-email/">Mailtrap's Guide on How to Build HTML Email</a> is live on our blog</p>
        <img src="cid:logo">
        </body>
    </html>'
    )
    ->embed(fopen('https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-new-logo.svg', 'r'), 'logo', 'image/svg+xml')
    ;
   
    // Headers
    $email->getHeaders()
    ->addTextHeader('X-Message-Source', 'domain.com')
    ->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client')) // the same as addTextHeader
    ;
   
    // Custom Variables
    $email->getHeaders()
    ->add(new CustomVariableHeader('user_id', '45982'))
    ->add(new CustomVariableHeader('batch_id', 'PSJ-12'))
    ;
   
    // Category (should be only one)
    $email->getHeaders()
    ->add(new CategoryHeader('Integration Test'))
    ;
   
try {
    $response = $mailtrap->sending()->emails()->send($email); // Email sending API (real)
   
    var_dump(ResponseHelper::toArray($response)); // body (array)
} catch (Exception $e) {
    echo 'Caught exception: ',  $e->getMessage(), "\n";
}

// OR send email to the Mailtrap SANDBOX

try {
    $response = $mailtrap->sandbox()->emails()->send($email, 1000001); // Required second param -> inbox_id

    var_dump(ResponseHelper::toArray($response)); // body (array)
} catch (Exception $e) {
    echo 'Caught exception: ',  $e->getMessage(), "\n";}

Note that this is a full example including CC, Bcc, Custom Variables and more. Of course, you can tone it down for your purposes. 

Send email with attachment

Again, I’ll assume that your contact form collets event-goers, and I’ll show you how to send an email via API with an event ticket as an attachment. 

I’ll refactor the Mailtrap PHP SDK script to include an email attachment and use the attach method available in the Symfony\Component\Mime\Email class. Also, I’ll assume you have a file named event_ticket.pdf located in the same directory as your script. 

<?php

use Mailtrap\Config;
use Mailtrap\EmailHeader\CategoryHeader;
use Mailtrap\EmailHeader\CustomVariableHeader;
use Mailtrap\Helper\ResponseHelper;
use Mailtrap\MailtrapClient;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\UnstructuredHeader;

require __DIR__ . '/vendor/autoload.php';

// your API token from here https://mailtrap.io/api-tokens
$apiKey = getenv('MAILTRAP_API_KEY');
$mailtrap = new MailtrapClient(new Config($apiKey));

$email = (new Email())
    ->from(new Address('example@your-domain-here.com', 'Mailtrap Test'))
    ->replyTo(new Address('reply@your-domain-here.com'))
    ->to(new Address('email@example.com', 'Jon'))
    ->priority(Email::PRIORITY_HIGH)
    ->cc('mailtrapqa@example.com')
    ->addCc('staging@example.com')
    ->bcc('mailtrapdev@example.com')
    ->subject('Best practices of building HTML emails')
    ->text('Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap's Guide on How to Build HTML Email is live on our blog')
    ->html(
        '<html>
        <body>
        <p><br>Hey</br>
        Learn the best practices of building HTML emails and play with ready-to-go templates.</p>
        <p><a href="https://mailtrap.io/blog/build-html-email/">Mailtrap's Guide on How to Build HTML Email</a> is live on our blog</p>
        <img src="cid:logo">
        </body>
    </html>'
    )
    ->embed(fopen('https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-new-logo.svg', 'r'), 'logo', 'image/svg+xml')
    // Attaching the event ticket
    ->attachFromPath(__DIR__ . '/event_ticket.pdf', 'Event Ticket.pdf', 'application/pdf');

// Headers
$email->getHeaders()
    ->addTextHeader('X-Message-Source', 'domain.com')
    ->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client'))
    ;

// Custom Variables
$email->getHeaders()
    ->add(new CustomVariableHeader('user_id', '45982'))
    ->add(new CustomVariableHeader('batch_id', 'PSJ-12'))
    ;

// Category (should be only one)
$email->getHeaders()
    ->add(new CategoryHeader('Integration Test'))
    ;

try {
    $response = $mailtrap->sending()->emails()->send($email); // Email sending API (real)
   
    var_dump(ResponseHelper::toArray($response)); // body (array)
} catch (Exception $e) {
    echo 'Caught exception: ',  $e->getMessage(), "\n";
}

// OR send email to the Mailtrap SANDBOX

try {
    $response = $mailtrap->sandbox()->emails()->send($email, 1000001); // Required second param -> inbox_id

    var_dump(ResponseHelper::toArray($response)); // body (array)
} catch (Exception $e) {
    echo 'Caught exception: ',  $e->getMessage(), "\n";}

How to send emails using PHP mail()

The PHP mail() function is a native PHP method for sending emails. Despite its direct access and ease of use, its notable limitations that have diminished its popularity. 

The limitations include:

  • Dependency on the server’s mail configuration, meaning it sends emails directly from your server. 
  • That setup often leads to emails being marked as spam or outright rejected by email providers. 
  • mail() lacks sophisticated features like email tracking and is susceptible to email injection attacks.

Due to these issues, I strongly advise against relying on mail(). However, for educational purposes, here’s how the mail() function is typically used:

<?php
mail($to, $subject, $message, $headers, $parameters);
?>

Parameters explained

  • $to: The recipient’s email address, which must be a valid email format like user@example.com or formatted with a name (User user@example.com) according to RFC 2822.
  • $subject: The subject line of the email.
  • $message: The main body text of your email, which should use Carriage Return Line Feed (CRLF, \r\n) to separate lines. Keep lines under 70 characters.
  • $headers: Essential for defining the From address. If omitted, PHP throws an error. Additional headers can include CC or BCC, structured as an array or a single string separated by CRLFs.
  • $parameters: Used to specify additional settings that are defined in the sendmail_path configuration.

Here’s an example of how to send a plain text email. 

<?php
$to = "somebody@example.com";
$subject = "My subject";
$txt = "Hello world!";
$headers = "From: webmaster@example.com" . "\r\n" .
"CC: somebodyelse@example.com";

mail($to, $subject, $txt, $headers);
?>

And if you want to send an HTML email, here’s the usual setup. 

// Message
$message = '
<html>
<head>
  <title>Review Request Reminder</title>
</head>
<body>
  <p>Here are the cases requiring your review in December:</p>
  <table>
    <tr>
      <th>Case title</th><th>Category</th><th>Status</th><th>Due date</th>
    </tr>
    <tr>
      <td>Case 1</td><td>Development</td><td>pending</td><td>Dec-20</td>
    </tr>
    <tr>
      <td>Case 1</td><td>DevOps</td><td>pending</td><td>Dec-21</td>
    </tr>
  </table>
</body>
</html>
';

To ensure correct formatting, there are two methods set the Content-type header appropriately. 

#1 Method

$headers = "MIME-Version: 1.0" . "\r\n";
$headers .= "Content-type:text/html;charset=UTF-8" . "\r\n";

#2 Method

$headers['MIME-Version'] = '1.0';
$headers['Content-type'] = 'text/html; charset=iso-8859-1';

Critical considerations:

  • Using mail(), you face potential deliverability challenges. 
  • Emails sent may bypass SPF and DKIM settings, leading to them being flagged as spam by recipient MTAs. 
  • Tracking delivery failures is problematic, as bounce-back messages are often not returned

PHP contact form doesn’t send emails: how to fix the issue?

Contact forms in PHP either use mail() function or SMTP authentication via PHPMailer to send emails. This means some common issues you may come across are connected to either malfunctioning of the mail() function, or to the incorrect settings of the SMTP.  There is a bare minimum checklist of what you can do to troubleshoot:

Code should match the form method

When you set the form method to POST, then make sure you use $_POST to look for your form values. Otherwise, if you set it to GET or didn’t set it at all, then you need to use $_GET to look for your form values. But as mentioned earlier, it’s safer to use POST. 

Configure the localhost mail server properly

If your workstation is local and you develop locally using WAMP, MAMP, or XAMPP, an email server might not be installed on your workstation. However, it is obligatory to install since PHP cannot send mail by default.

Use a different mailing package

PHP’s built-in mail() function is simple but it has quite a few disadvantages. Consider the alternatives that offer more flexibility and fewer errors like PHPMailer or Symfony Mailer.

Enable PHP’s custom mail.log

This would not record the complete SMTP interaction, but at least function call parameters and invocation script.

ini_set("mail.log", "/tmp/mail.log");
ini_set("mail.add_x_header", TRUE);

Wrapping up

When all is said and done, now you’re fully equipped to create and set in motion a PHP email form. Not only date, but you’ll be making user data safe. 

And if you’re interested in more PHP-related articles, we’ve got a handful of them waiting for you on our blog, such as:

]]>
https://mailtrap.io/blog/php-email-contact-form/feed/ 5
How to Set up Email Verification in PHP via a Verification Token: Complete Guide https://mailtrap.io/blog/php-email-verification/ Wed, 19 Jun 2024 10:00:02 +0000 https://mailtrap.io/?p=31142 Email verification is the process of ensuring an email address exists and can receive emails. Whereas, email validation checks if the address is properly formatted; that is – written according to specific standards (e.g. UTF-8). 

In this article, I’ll talk about PHP email verification and how to use it for web development and user authentication via a verification token. The article involves a few micro tutorials, including:

So, let’s get to it. 

Ready to deliver your emails?
Try Mailtrap for Free

Setting up email sending

To send verification emails, you can use PHP’s built-in mail() function or a library like PHPMailer, which offers more features and better reliability.

Since I want to make the tutorial as safe and production-ready as possible, I’ll be using ‘PHPMailer’. Check the code to install PHPMailer via Composer:

composer require phpmailer/phpmailer

As for the sending itself, I’ll be using Mailtrap API/SMTP, the SMTP method. 

Why use Mailtrap API/SMTP? It’s an email-sending solution for developer and product teams, focused on fast delivery and high inboxing rates for transactional and promo emails. Mailtrap offers:

  • Ready-made configuration settings for various languages, PHP & Laravel included.
  • SMTP and API with SDKs in major languages, ofc, PHP included. 
  • Industry-best analytics. 
  • 27/7 Human support, and fast track procedure for urgent cases. 

All that allows you to bootstrap the email verification process, and keep it safe and stable for all. 

Moving on to the settings to configure PHPMailer with Mailtrap:

$phpmailer = new PHPMailer();
$phpmailer->isSMTP();
$phpmailer->Host = 'live.smtp.mailtrap.io';
$phpmailer->SMTPAuth = true;
$phpmailer->Port = 587;
$phpmailer->Username = 'api';
$phpmailer->Password = 'YOUR_MAILTRAP_PASSWORD';

Here’s the PHPMailer setup:

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require 'vendor/autoload.php';

function sendVerificationEmail($email, $verificationCode) {
    $mail = new PHPMailer(true);

    try {
        // Server settings
        $mail->isSMTP();
        $mail->Host = 'live.smtp.mailtrap.io';
        $mail->SMTPAuth = true;
        $mail->Username = 'api';
        $mail->Password = 'YOUR_MAILTRAP_PASSWORD';
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
        $mail->Port = 587;

        // Recipients
        $mail->setFrom('youremail@example.com', 'Your Website');
        $mail->addAddress($email);

        // Content
        $mail->isHTML(false);
        $mail->Subject = 'Email Verification';
        $mail->Body    = "Your verification code is: $verificationCode";

        $mail->send();
        return true;
    } catch (Exception $e) {
        return false;
    }
}

Note that the code above doesn’t send the verification token (click here to jump to the code snippet with the verification token). It’s only an example of how to set up Mailtrap SMTP and define the verification function. Here’s a quick breakdown of key points:

  • PHPMailer and Exception classes get imported.
  • sendVerificationEmail($email, $verificationCode) is the function definition. 
  • A new PHPMailer object is created. 
  • The try-catch block handles exceptions during email sending.
  • The server settings are set to Mailtrap as per the exemplary configuration. 
  • The email content is set to isHTML(false) for plain text. 

Tips: 

  • The email content can be refactored to HTML. 
  • Due to throughput limitations, you should avoid using gmail.com as a signup form SMTP relay. But if you really want to create a mailer PHP file and send via Gmail, check this tutorial

Creating a registration form

The below is a simple registration form, it contains the header and user account information (username, email, and password). 

It doesn’t have any CSS stylesheet or div class since this is only an example. 

However, I’d advise you to include these on production and align them with the design language of your brand. Otherwise, your form may look unprofessional and users would be reluctant to engage with it. 

<!DOCTYPE html>
<html>
<head>
    <title>Register</title>
</head>
<body>
    <form action="register.php" method="post">
        <label>Username:</label>
        <input type="text" name="username" required>
        <br>
        <label>Email:</label>
        <input type="email" name="email" required>
        <br>
        <label>Password:</label>
        <input type="password" name="password" required>
        <br>
        <input type="submit" name="register" value="Register">
    </form>
</body>
</html>

Bonus Pro TipConsider using JavaScript with your forms 

If you want a full tutorial on how to create a PHP contact form that includes reCaptcha, check the video below ⬇️. 

  • JS can validate user input in real time, providing immediate feedback on errors without needing to reload the page. 
  • By catching errors on the client side, JS can reduce the number of invalid requests sent to the server, thereby reducing server load and improving performance for each session.
  • Using AJAX, JS can send and receive data from the server without reloading the page, providing a smoother user experience.

Now, I’ll move to email address verification.  

Email address verification

Here’s a simple script to check for the domain and the MX record. It basically allows you to verify email by performing an MX lookup.

<?php

// This method checks if the domain part of the email address has a functioning mail server.

$email = "user@example.com";

list($user, $domain) = explode(separator:"@", $email)

if (filter_var($email, filter:FILTER_VALIDATE_EMAIL) && getmxrr($domain, &hosts: $mxhosts)){
    echo "Valid email address with a valid mail server" . PHP_EOL;
} else {
    echo "Invalid email address or no valid mail server found" . PHP_EOL;
}

However, the script doesn’t send email for user activation and authentication. Also, it doesn’t store any data in MySQL. 

For that, I’ll do the following in the next sections: 

  • Generate a verification token 
  • Create a PHP MySQL schema to store the credentials from the registration form
  • Send the verification email with the token
  • Verify the verification token

Tip: Similar logic can be applied to a logout/login form. 

Generating verification token

A verification token is a unique string generated for each user during registration. This token is included in the verification email and there are two methods to generate it.

Method 1

The first method leverages the bin2hex command to create a random token with the parameter set to (random_bytes(50))

$token = bin2hex(random_bytes(50));

Method 2

Alternatively, you can generate the token with the script below. And I’ll be using that script in the email-sending script.

<?php
function generateVerificationCode($length = 6) {
    $characters = '0123456789';
    $code = '';
    for ($i = 0; $i < $length; $i++) {
        $code .= $characters[rand(0, strlen($characters) - 1)];
    }
    return $code;
}
?>

Storing verification token

Before sending the verification email, it’s vital to ensure you properly handle and store user data. I’ll use a simple SQL schema to create the users table and store the generated token in the database along with the user’s registration information.

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    password VARCHAR(255) NOT NULL,
    token VARCHAR(255) DEFAULT NULL,
    is_verified TINYINT(1) DEFAULT 0
);

Quick breakdown: 

The script above creates a users table with the following columns:

  • id – Unique identifier for each user, automatically incremented.
  • username – The user’s username; it cannot be null.
  • email – The user’s email address; it cannot be null.
  • password – The user’s password (hashed); it cannot be null.
  • token – A verification token, which can be null.
  • is_verified – A flag indicating whether the user is verified (0 for not verified, 1 for verified), with a default value of 0.

Sending verification token 

Overall, the script below is amalgamation of everything previously discussed in the article and it’s designed to: 

  • Generate a random numeric verification code. 
  • Send the verification email to a specified email address using PHPMailer.
  • Configure the email server settings. 
  • Handle potential errors. 
  • Provide feedback on whether the email was successfully sent.

Note that the script is geared towards Mailtrap users and it leverages the SMTP method. 

<?php

require 'vendor/autoload.php';

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP
use PHPMailer\PHPMailer\Exception;

//Function to generate a random verification code
1 usage
function generateVerificationCode($length = 6) {
    $characters = '0123456789';
    $code = '';
    for ($i = 0; $i < $length; $i++) {
        $code .= $characters[rand(0, strlen($characters) - 1)];
    }
    return $code;
}

// Function to send a verification email using PHPMailer
1 usage
function sendVerificationEmail($email, $verificationCode) {
    $mail = new PHPMailer (exception: true);

    try {
        // Server settings
        $mail ->SMTPDebug = SMTP::DEBUG_OFF; // Set to DEBUG_SERVER for debugging
        $mail ->isSMTP();
        $mail ->Host = 'live.smtp.mailtrap.io'; // Mailtrap SMTP server host 
        $mail ->SMTPAuth = true;
        $mail ->Username = 'api'; // Your Mailtrap SMTP username
        $mail ->Password = 'YOUR_MAILTRAP_PASSWORD'; // Your Mailtrap SMTP password
        $mail ->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // Enable TLS encryption
        $email ->Port = 587; // TCP port to connect to

        //Recipients
        $mail->setFrom(address:'mailtrapclub@gmail.com', name:"John Doe"); //Sender's email and name
        $mail->addAddress($email); // Recipient's email

        //Content
        $mail->isHTML(isHTML:false); //Set to true if sending HTML email
        $mail->Subject = 'Email Verification';
        $mail->Body = "Your verification code is: $verificationCode";

        $mail->send();
        return true;
    }catch (Exception $e) {
        return false;
    }
}

//Example usage
$email = "mailtrapclub+test@gmail.com"
$verificationCode = generateVerificationCode();

if (sendVerificationEmail($email,$verificationCode)){
    echo "A verification email has been sent to $email. Please check your inbox and enter the code to verrify your email." . PHP_EOL;
} else {
    echo "Failed to send the verification email. Please try again later." . PHP_EOL;
}

Verifying verification token

Yeah, the title is a bit circular, but that’s exactly what you need. The script below enables the “verification of verification” flow 😀 that moves like this: 

  1. A user hits the verification link. 
  2. The token gets validated.
  3. The user’s email is marked as verified in the database. 
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "user_verification";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

if (isset($_GET['token'])) {
    $token = $_GET['token'];

    $stmt = $conn->prepare("SELECT * FROM users WHERE token=? LIMIT 1");    $stmt->bind_param("s", $token);    $stmt->execute();
    $result = $stmt->get_result();
    if ($result->num_rows > 0) {
        $user = $result->fetch_assoc();        $stmt->close();
        $stmt = $conn->prepare("UPDATE users SET is_verified=1, token=NULL WHERE id=?");        $stmt->bind_param("i", $user['id']);

        if ($stmt->execute() === TRUE) {
            echo "Email verification successful!";
        } else {
            echo "Error: " . $conn->error;
        }        $stmt->close();
    } else {
        echo "Invalid token!";
    }
}

$conn->close();
?>

Wrapping up

I’ve walked through PHP email verification and the use of the verification token. We’ve covered everything from setting up the environment, to sending and verifying emails. 

Following these steps will help you ensure the authenticity of your users and maintain a clean and secure user database.

Happy verification!

]]>
Node.js Contact Form: How to Create One, Validate Data, and Send Emails https://mailtrap.io/blog/node-js-contact-form/ Fri, 31 May 2024 11:49:04 +0000 https://mailtrap.io/?p=30300 In this step-by-step tutorial, I’ll show you how to create a Node.js contact form, give it a personal touch, retrieve and validate data from it, and then send emails through the form via SMTP or API.

Note: you’ll need Node.js 6+ or any version released since May 2018 installed on your machine for the provided code snippets to work.

Ready to deliver your emails?
Try Mailtrap for Free

How to create a Node.js contact form

As you’re reading this article, I’m assuming you know how to install Node.js and create a new project, so allow me to keep it brief and get straight into setting up the project.

However, to freshen up your knowledge, you can check out this article on installing Node.js.

Setting up the project

First things first, let’s install some dependencies for our backend by opening the terminal and entering the following commands:

  • mkdir contact-form-test && cd contact-form-test – By running this command, we create a folder for our project, ensuring our project file will be within it instead of the current directory.
  • npm init -y – This will create a package.json file that manages project dependencies and configurations.
  • npm i express nodemailer – We need to install Express.js library and Nodemailer module as we will need them for setting up our server and sending emails with SMTP, respectively.
  • npm i -D nodemon – This is a dev dependency that automates the process of restarting our server whenever we change our code, allowing us to see the changes we made without having us manually restart the server.

Once you install all the dependencies, your package.json file should look something like this:

{
  "name": "contact-form-test",          // Project name
  "version": "1.0.0",                   // Project version
  "description": "",                    // Description of the project
  "main": "server.js",                  // Entry point file of the project
  "scripts": {
    "dev": "nodemon --watch public --watch server.js --ext js,html,css", // Script to run the server with nodemon for development
    "start": "node server.js"           // Script to start the server normally
  },
  "keywords": [],                       // Keywords related to the project
  "author": "",                         // Author of the project
  "license": "ISC",                     // License type
  "dependencies": {
    "express": "^4.19.2",               // Express web server framework
    "nodemailer": "^6.9.13"             // Nodemailer for sending emails via SMTP
  },
  "devDependencies": {
    "nodemon": "^3.1.0"                 // Nodemon for automatically restarting the server on code changes
  }
}

Bonus tips:

  • npm i dotenv – Although optional, this command installs the dotenv package, which loads environment variables where you can safely store your authentication credentials such as an API key. All you have to do is run the command, create an .env file in the root of your project directory, and paste your desired creds in there.
  • Starting from v20.6.0, Node.js has built-in support for .env files for configuring environment variables. So it is not necessary to use the dotenv package, but a lot of projects still depend on dotenv, so it’s still the de facto standard.

Configuring the server

Next, in our project folder, let’s create a new .js file called server.js which we refer to in the production script from the package.json file. 

Then, simply paste the following code snippet into the server.js file:

const express = require('express');
const app = express();
const path = require('path');


const PORT = process.env.PORT || 3000;


// Middleware
app.use(express.static('public'));
app.use(express.json());


app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'contactform.html'));
});


app.post('/send-email', (req, res) => {
    console.log(req.body);
    res.send('Data received');
});


app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

Note: Later in the article, we’ll use the server.js file to add Nodemailer as a transport via SMTP and an API logic to send emails through our contact form.

Creating the contact form

Now, let’s create a new public folder called public for the static files (style.css, contactform.html, and app.js) we’re going to be using for this contact form.

In the contactform.html file, enter the following code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"> <!-- Specifies the character encoding for the HTML document -->
    <link rel="stylesheet" href="/style.css"> <!-- Link to external CSS file for styling -->
    <link rel="preconnect" href="https://fonts.gstatic.com"> <!-- Preconnect to load fonts faster -->
    <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet"> <!-- Google fonts link for 'Poppins' font -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Responsive design meta tag -->
    <title>Contact Form</title> <!-- Title of the document shown in the browser tab -->
</head>
<body>
    <div class="form-container"> <!-- Container for the form to style it specifically -->
        <form class="contact-form"> <!-- Form element where user inputs will be submitted -->
            <h2>CONTACT</h2> <!-- Heading of the form -->
            <input type="text" id="name" placeholder="Full name"><br> <!-- Input field for name -->
            <input type="email" id="email" placeholder="Email"><br> <!-- Input field for email, validates email format -->
            <input type="text" id="subject" placeholder="Subject"><br> <!-- Input field for subject -->
            <textarea id="message" placeholder="Message" cols="30" rows="10"></textarea><br> <!-- Textarea for longer message input -->
            <input type="submit" class="submit" value="Send Message"> <!-- Submit button to send the form data -->
        </form>
    </div>
    <script src="/app.js"></script> <!-- Link to external JavaScript file for scripting -->
</body>
</html>

I’ve added annotations in this code snippet as well to help you navigate through it, but feel free to delete them for a cleaner-looking code. 🙂

Styling the contact form

How about we tackle the frontend for a bit and add a personal touch to our contact form? 

In the style.css file, enter the following code which will make our contact form prettier:

/* Global styles for all elements to ensure consistency */
* {
    margin: 0;                          /* Remove default margin */
    padding: 0;                         /* Remove default padding */
    box-sizing: border-box;             /* Include padding and border in the element's total width and height */
    font-family: 'Poppins', sans-serif; /* Set a consistent font family throughout the app */
}


/* Styling for the html and body elements */
html, body {
    background: #c0b7b7;                /* Set the background color for the entire page */
}


/* Container for the form providing relative positioning context */
.form-container {
    position: relative;                 /* Positioning context for absolute positioning inside */
    left: 20%;                          /* Position the container 20% from the left side of the viewport */
    width: 60%;                         /* Set the width of the container to 60% of the viewport width */
    height: 100vh;                      /* Set the height to be 100% of the viewport height */
    background-color: white;            /* Set the background color of the form container */
}


/* Styling for the contact form itself */
.contact-form {
    position: absolute;                 /* Position the form absolutely within its parent container */
    top: 10%;                           /* Position the form 10% from the top of its container */
    left: 10%;                          /* Position the form 10% from the left of its container */
    width: 80%;                         /* The form width is 80% of its container */
    min-height: 600px;                  /* Minimum height for the form */
}


/* Styling for input fields and textarea within the form */
input, textarea {
    width: 100%;                        /* Make input and textarea elements take up 100% of their parent's width */
    margin-top: 2rem;                   /* Add top margin to space out the elements */
    border: none;                       /* Remove default borders */
    border-bottom: 1px solid black;     /* Add a bottom border for a minimalistic look */
    padding: 10px; /* Add padding for better readability */
}


/* Styling for the submit button */
.submit {
    border: 1px solid black;            /* Add a solid border around the submit button */
    padding: 1rem;                      /* Add padding inside the button for better clickability */
    text-align: center;                 /* Center the text inside the button */
    background-color: white;            /* Set the background color of the button */
    cursor: pointer;                    /* Change the cursor to a pointer to indicate it's clickable */
}


/* Styling for the submit button on hover */
.submit:hover {
    opacity: 0.6;                       /* Change the opacity when hovered to give a visual feedback */
}

To see how your contact form looks, you can save the file and enter the following command in your terminal:

npm run dev

Then, you should see the message saying that your contact form is being hosted on the custom port you defined in your .env file or the default port 3000, which allows traffic to reach it. 

Finally, paste the following link in your browser’s URL bar: http://localhost:3000/ and you should see your contact form in its full glory. 

How to collect data from a Node.js contact form

To collect data from our Node.js contact form, we will add functionality for handling form submissions using JavaScript, which will capture form data and send it to the server without reloading the page.

For this, we’ll use an AJAX request, which allows us to avoid a full-page reload.

I’ve made this easy for you, so all you have to do is navigate to your app.js file and enter the following code:

const contactForm = document.querySelector('.contact-form');
const name = document.getElementById('name');
const email = document.getElementById('email');
const subject = document.getElementById('subject');
const message = document.getElementById('message');


contactForm.addEventListener('submit', (e) => {
    e.preventDefault();  // Prevent the default form submission


    const formData = {
        name: name.value,
        email: email.value,
        subject: subject.value,
        message: message.value
    };


    try {
        const response = await fetch('/send-email', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(formData)
        });


        // Wait for JSON response to be parsed
        const result = await response.json();


        if (!response.ok) {
            // If the response is not OK, handle it by showing an alert
            alert(`Failed to send message: ${result.message}`);
            return;  // Exit the function early if there's an error
        }


        // Check application-specific status from JSON when response is OK
        if (result.status === 'success') {
            alert('Email sent');


            // Reset form fields after successful submission
            name.value = '';
            email.value = '';
            subject.value = '';
            message.value = '';
        } else {
            // Handle application-level failure not caught by response.ok
            alert('Operation failed: ' + result.message);
        }


    } catch (error) {
        // Handle any exceptions that occur during fetch
        console.error('Error:', error);
        alert('Network error or cannot connect to server');
    }


});

Note: I used the fetch API here which is a more modern alternative to ‘XMLHttpRequest’ and leverages async/await syntax for better error handling.

How to validate data from a contact form

To validate data from a contact form in Node.js, you can either use:

  • Deep email validator – A comprehensive, MIT-certified, dependency package that makes sure an email is valid by running it through several different checks. It validates RegEx, common typos, disposable email blacklists, DNS records, and SMTP server responses.
  • Regex expressions – Patterns used to match character combinations in strings, ensuring that inputs conform to a predefined format. They provide a more basic level of validation compared to dependency packages like Deep email validator.

Deep email validator module

To install the Deep email validator, I typically use the npm command or Yarn:

npm i deep-email-validator

# or

yarn add deep-email-validator

Then after importing const { validate } = require('deep-email-validator'); simply add the following code in your /send-email controller:

// Endpoint to handle form submission and send email
app.post('/send-email', async (req, res) => {
   const { name, email, subject, message } = req.body;


   if (!name || !email || !subject || !message) {
       return res.status(400).json({ status: 'error', message: 'Missing required fields!' })
   }


   // Validate the email
   const validationResult = await validate(email);


   if (!validationResult.valid) {
       return res.status(400).json({
           status: 'error',
           message: 'Email is not valid. Please try again!',
           reason: validationResult.reason
       });
   }


   // Email sending logic


   // Placeholder response for a successful email submission
   res.status(200).json({
       status: 'success',
       message: 'Email successfully sent'
   });
});

For more information on Deep email validator, consult the official GitHub page.

Regular expressions for email validation

If you’d rather run basic ReGex checks against the email IDs instead of using a dependency package, you can paste the following code into your project file (e.g., server.js):

const emailRegex = 

/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/


function isEmailValid(email) {
    // Check if the email is defined and not too long
    if (!email || email.length > 254) return false;

    // Use a single regex check for the standard email parts
    if (!emailRegex.test(email)) return false;

    // Split once and perform length checks on the parts
    const parts = email.split("@");
    if (parts[0].length > 64) return false;

    // Perform length checks on domain parts
    const domainParts = parts[1].split(".");
    if (domainParts.some(part => part.length > 63)) return false;

    // If all checks pass, the email is valid
    return true;
}

Then in your /send-email route add the following code to perform email validation:

// Endpoint to handle form submission and send email
app.post('/send-email', async (req, res) => {
   const { name, email, subject, message } = req.body;


   if (!name || !email || !subject || !message) {
       res.status(400).json({ status: 'error', message: 'Missing required fields!' })
   }


   // Validate the email
   if (!isEmailValid(email)) {
       return res.status(400).json({
           status: 'error',
           message: 'Email is not valid. Please try again!'
       });
   }


   // Email sending logic


   // Placeholder response for a successful email submission
   res.status(200).json({
       status: 'success',
       message: 'Email successfully sent'
   });
});

For more details on validating emails in Node.js, feel free to consult our dedicated article, where you can also learn how to verify emails by sending an activation link/code the end users activate from their inbox.

How to add reCAPTCHA

Now, let’s add a reCAPTCHA to our Node.js contact form to make sure no robots fill it out. 🤖

First, you need to get your ‘SITE KEY’ and ‘SECRET KEY’ from the Google reCAPTCHA admin console.

In this example we will be using Challenge (v2), so be sure to select it during creation and leave the default “I’m not a robot Checkbox”. 

Also, since we are testing from a local computer we need to add ‘localhost’ to allowed domains during creation. Note: if you are in production you would need to use your own domains. And to use this code, simply copy your SITE KEY and SECRET KEY.

Next, in our contactform.html, add the following code snippet just before the closing of the head section:

<script src="https://www.google.com/recaptcha/api.js" async defer></script> <!-- Load google recaptcha API -->

Inside our form, we can add the reCAPTCHA element after the textarea like this: 

<div class="g-recaptcha" data-sitekey="YOUR_SITE_KEY"></div><br> <!-- Google recaptcha element -->

Important: don’t forget to replace YOUR_SITE_KEY with your actual SITE KEY.

Then, we need to update our app.js inside the public folder to pass the reCAPTCHA token to our formData so the backend can verify it. To do this, after the e.preventDefault(); paste the following code :

// Get the reCAPTCHA token
   const recaptchaToken = grecaptcha.getResponse();


   if (!recaptchaToken) {
       alert('Please complete the reCAPTCHA');
       return;  // Exit the function early if reCAPTCHA is not completed
   }


   const formData = {
       name: name.value,
       email: email.value,
       subject: subject.value,
       message: message.value,
       'g-recaptcha-response': recaptchaToken
   };

To verify the reCAPTCHA on the server side, we need to make a request. For this, we can use the built in Node.js ‘https’, or a package like ‘node-fetch’ or ‘axios’.

And since Node.js ver 18, the fetch method is available on the global scope natively, so let’s use ‘fetch’.

First, in our server.js let’s create a function to verify the reCAPTCHA (make sure to add the SECRET KEY as RECAPTCHA_SECRET_KEY in environment variables else the verification will not succeed):

// Function to verify reCAPTCHA
async function verifyRecaptcha(token) {
   const recaptchaSecret = process.env.RECAPTCHA_SECRET_KEY;
   const recaptchaUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${recaptchaSecret}&response=${token}`;


   const response = await fetch(recaptchaUrl, { method: 'POST' });
   const result = await response.json();


   return result.success;
}

Then in our /send-email endpoint we can update our code to this:

// Endpoint to handle form submission and send email
app.post('/send-email', async (req, res) => {
   const { name, email, subject, message, 'g-recaptcha-response': recaptchaToken } = req.body;


   if (!name || !email || !subject || !message || !recaptchaToken) {
       res.status(400).json({ status: 'error', message: 'Missing required fields!' })
   }


   // Verify the reCAPTCHA token
   const isRecaptchaValid = await verifyRecaptcha(recaptchaToken);
   if (!isRecaptchaValid) {
       return res.status(400).json({
           status: 'error',
           message: 'reCAPTCHA verification failed. Please try again.'
       });
   }


   // Validate the email
   const validationResult = await validate(email);


   if (!validationResult.valid) {
       return res.status(400).json({
           status: 'error',
           message: 'Email is not valid. Please try again!',
           reason: validationResult.reason
       });
   }


   // Email sending logic


   // Placeholder response for a successful email submission
   res.status(200).json({
       status: 'success',
       message: 'Email successfully sent'
   });
});

Send email from Node.js contact form using SMTP

Now that we have our Node.js contact form flow in place, let’s add an email-sending functionality to it. 

For this, we’ll need two things:

  • Nodemailer – If you’re reading this article, I’m guessing you already have Nodemailer installed, but if you don’t, here’s how to install it.
  • SMTP credentials – With Nodemailer, you can leverage any SMTP server like, for example, Gmail. However, Gmail has some significant limitations, about which you can find out more in our dedicated Nodemailer Gmail tutorial.

So, to overcome Gmail limitations, we’ll set up Mailtrap Email Sending instead, as the SMTP service in our Nodemailer transporter object. It offers an infrastructure with high deliverability rates by default and by design, plus it’s easy to use.

First, create a free Mailtrap account and verify your domain. It only takes a couple of minutes, and you can watch the video we’ve prepared for you as a step-by-step tutorial.

Then, proceed to the Sending Domains section, choose your domain, and under Integration, select your preferred stream (Transactional, in this case). There, you will find your SMTP credentials, which you can easily paste into the Nodemailer config file.

Mailtrap Email Sending Integration page SMTP/API

Speaking of Nodemailer configuration, let’s insert it into the server.js file beneath the comment // Create a transporter object, like so:

// Create a transporter object
const transporter = nodemailer.createTransport({
  host: 'live.smtp.mailtrap.io',
  port: 587,
  secure: false, // use false for STARTTLS; true for SSL on port 465
  auth: {
    user: '1a2b3c4d5e6f7g',
    pass: '1a2b3c4d5e6f7g',
  }
});

// Configure the mailoptions object
const mailOptions = {
  from: 'yourusername@email.com',
  to: 'yourfriend@email.com',
  subject: 'Sending Email using Node.js',
  text: 'That was easy!'
};

// Send the email
transporter.sendMail(mailOptions, (error, info) =>{
  if (error) {
    console.log('Error:' error);
    return res.status(500).json({ status: 'error', message: 'Failed to send email due to server error.' });
  } else {
    console.log('Email sent: ' + info.response);
    return res.status(200).json({
               status: 'success',
               message: 'Email successfully sent'
           });
  }
});

Then, just insert your Mailtrap credentials into their respectable fields, including host, port, user, and pass.

In the end, your server.js file should look something like this:

require('dotenv').config();
const express = require('express');
const { validate } = require('deep-email-validator');
const path = require('path');


const app = express();
const PORT = process.env.PORT || 3000;


// Middleware
app.use(express.static('public'));
app.use(express.json());


app.get('/', (req, res) => {
   res.sendFile(path.join(__dirname, 'public', 'contactform.html'));
});


// Function to verify reCAPTCHA
async function verifyRecaptcha(token) {
   const recaptchaSecret = process.env.RECAPTCHA_SECRET_KEY;
   const recaptchaUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${recaptchaSecret}&response=${token}`;


   const response = await fetch(recaptchaUrl, { method: 'POST' });
   const result = await response.json();


   return result.success;
}


// Endpoint to handle form submission and send email
app.post('/send-email', async (req, res) => {
   const { name, email, subject, message, 'g-recaptcha-response': recaptchaToken } = req.body;


   if (!name || !email || !subject || !message || !recaptchaToken) {
       return res.status(400).json({ status: 'error', message: 'Missing required fields!' })
   }


   // Verify the reCAPTCHA token
   const isRecaptchaValid = await verifyRecaptcha(recaptchaToken);
   if (!isRecaptchaValid) {
       return res.status(400).json({
           status: 'error',
           message: 'reCAPTCHA verification failed. Please try again.'
       });
   }


   // Validate the email
   const validationResult = await validate(email);


   if (!validationResult.valid) {
       return res.status(400).json({
           status: 'error',
           message: 'Email is not valid. Please try again!',
           reason: validationResult.reason
       });
   }


   // Email sending logic
   // Create a transporter object
   const transporter = nodemailer.createTransport({
       host: process.env.SMTP_HOST,
       port: process.env.SMTP_PORT,
       secure: false, // use false for STARTTLS; true for SSL on port 465
       auth: {
           user: process.env.SMTP_USER,
           pass: process.env.SMTP_PASS,
       }
   });


   // Configure the mailoptions object
   const mailOptions = {
       from: process.env.EMAIL_FROM,
       to: process.env.EMAIL_TO,
       replyTo: email,
       subject: subject,
       text: `From: ${name}\nEmail:${email}\n\n${message}`
   };


   // Send the email
   transporter.sendMail(mailOptions, (error, info) => {
       if (error) {
           console.log('Error:', error);
           return res.status(500).json({ status: 'error', message: 'Failed to send email due to server error.' });


       } else {
           console.log('Email sent: ' + info.response);
           return res.status(200).json({
               status: 'success',
               message: 'Email successfully sent'
           });
       }
   });
});


app.listen(PORT, () => {
   console.log(`Server running on port ${PORT}`);
});

To safely store your Mailtrap credentials, you can use the environment variable, or the .env file, like so:

PORT=3200
SMTP_HOST='live.smtp.mailtrap.io'
SMTP_PORT=587
SMTP_USER='1a2b3c4d5e6f7g'
SMTP_PASS='1a2b3c4d5e6f7g'
EMAIL_FROM='yourusername@email.com'
EMAIL_TO='contactformrecipient@yourmail.com'
RECAPTCHA_SECRET_KEY='SECRET-KEY'

Send email from Node.js contact form using API

If you want to automate your contact form’s email-sending functionality, you can again rely on Mailtrap as it has a robust sending package. The package itself is regularly updated by a team of developers, lets you automate your sending process, and it’s super straightforward to use.

First, you’ll need a Mailtrap account, which, if you don’t already have one, you can create by following the instructions provided in the previous chapter.

Then, let’s install the Mailtrap package:

npm install mailtrap

# or, if you are using yarn:

yarn add mailtrap

Once it’s installed, you can use the following code snippet for email sending:

import { MailtrapClient } from "mailtrap"

/**
 * For this example to work, you need to set up a sending domain,
 * and obtain a token that is authorized to send from the domain.
 */

const TOKEN = "<YOUR-TOKEN-HERE>";
const SENDER_EMAIL = "<SENDER ADDRESS@YOURDOMAIN.COM>";
const RECIPIENT_EMAIL = "<RECIPIENT@EMAIL.COM>";

const client = new MailtrapClient({ token: TOKEN });

const sender = { name: "Mailtrap Test", email: SENDER_EMAIL };

client
  .send({
    from: sender,
    to: [{ email: RECIPIENT_EMAIL }],
    subject: "Hello from Mailtrap!",
    text: "Welcome to Mailtrap Sending!",
  })
 .then(response => {
    console.log("Email sent successfully:", response);
  })
  .catch(error => {
    console.error("Error sending email:", error);
  });

And for your convenience’s sake, here’s what your server.js file should look like:

require('dotenv').config();
const express = require('express');
const { MailtrapClient } = require('mailtrap');
const { validate } = require('deep-email-validator');
const path = require('path');


const app = express();
const PORT = process.env.PORT || 3000;


// Middleware
app.use(express.static('public'));
app.use(express.json());


app.get('/', (req, res) => {
   res.sendFile(path.join(__dirname, 'public', 'contactform.html'));
});


// Function to verify reCAPTCHA
async function verifyRecaptcha(token) {
   const recaptchaSecret = process.env.RECAPTCHA_SECRET_KEY;
   const recaptchaUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${recaptchaSecret}&response=${token}`;


   const response = await fetch(recaptchaUrl, { method: 'POST' });
   const result = await response.json();


   return result.success;
}


// Endpoint to handle form submission and send email
app.post('/send-email', async (req, res) => {
   const { name, email, subject, message, 'g-recaptcha-response': recaptchaToken } = req.body;


   if (!name || !email || !subject || !message || !recaptchaToken) {
       return res.status(400).json({ status: 'error', message: 'Missing required fields!' })
   }


   // Verify the reCAPTCHA token
   const isRecaptchaValid = await verifyRecaptcha(recaptchaToken);
   if (!isRecaptchaValid) {
       return res.status(400).json({
           status: 'error',
           message: 'reCAPTCHA verification failed. Please try again.'
       });
   }


   // Validate the email
   const validationResult = await validate(email);


   if (!validationResult.valid) {
       return res.status(400).json({
           status: 'error',
           message: 'Email is not valid. Please try again!',
           reason: validationResult.reason
       });
   }


   // Configure mailtrap client and define sender
   console.log(process.env.MAILTRAP_TOKEN);
   const client = new MailtrapClient({ token: process.env.MAILTRAP_TOKEN });
   const sender = { name: "NodeJS App", email: process.env.EMAIL_FROM };


   // Send email
   try {
       const response = await client.send({
           from: sender,
           to: [{ email: process.env.EMAIL_TO }],
           subject: subject,
           text: `From: ${name}\nEmail: ${email}\n\n${message}`,
       });


       console.log('Email sent: ', response.message_ids);
       res.status(200).json({
           status: 'success',
           message: 'Email successfully sent'
       });
   } catch (error) {
       console.log('Error:', error);
       res.status(500).json({ status: 'error', message: 'Failed to send email due to server error.' });
   }


});


app.listen(PORT, () => {
   console.log(`Server running on port ${PORT}`);
});

If you want to safely store your credentials in an environment variable, here’s what your .env should look like:

PORT=3200
MAILTRAP_TOKEN='7ff93fc2453461800734fb5c8bbe735d'
EMAIL_FROM='yourusername@email.com'
EMAIL_TO='contactformrecipient@yourmail.com'
RECAPTCHA_SECRET_KEY='SECRET-KEY'

And voila! Your Node.js contact form can now send emails via API.

Keep in mind that the code snippet I’ve shown you here is only for plain-text messages. If you wish to send HTML emails or add embedded images or attachments, please refer to the examples folder in the GitHub repository.

Wrapping up

And with that, we’ve come to the end of the line!

Now that you’ve got the ropes, you can style your Node.js contact form according to your artistic eye and make it function according to your application’s specific needs.

So, code away, and be sure to give our blog and YouTube channel a visit!

We have a plethora of helpful JavaScript-related articles, including:

As well as video tutorials:

]]>