“Your most unhappy customers are your greatest source of learning.” - Bill Gates

Wait wait wait, lets not take these words as a law just because Bill uttered them. However, lets establish that customer feedback is a key part in improving your product / service and in the end, growing your business.

Feedback may come in different forms, from different sources, from users with varied engagement and happiness with your product. One thing is sure, if you don’t have an easy way for your users to start a two-way communication with you regarding your service, chances are that you will miss a lot of good suggestions and business opportunities.

Most websites and services have a contact feature. Some may just implement it because it is something you should have, to look respectable. This is a bit sad. Most of us have good intentions though. In the end we want to make our customers happy, and save ourselves a lot of guessing work of what features our users appreciate, what we are lacking, and what we could improve.

In this tutorial we are going to build and setup an contact form using HTML5, CSS and PHP.

Just want the files?

Don't feel like reading and copy-pasting a lot?

Just head over to our download section and download the solution.

Our quick configuration guide will help you implement our solution in a couple of minutes.

Download Files

Implementing a communication channel

Since the usage of contact / mail forms are so high there’s a pound of plugin solutions for most of the content management systems and web frameworks these days. These may save you time, however, as we will show in this guide, building your own solution in HTML5, CSS and PHP does not take that much time and might well be worth the time.

We do not expect to know which solution is best in your case. Instead we will discuss the pros and cons of rolling your own contact functionality versus using a plugin solution.

When you’ve made up your mind about which road you want to take we provide a tutorial on how to build a PHP Contact form by your self, easily. If you have decided to go with a pre-built solution we will point you in the right direction, depending on what framework you are working in.

Custom built contact form vs plugin

One thing that is certainly positive with rolling your own contact form is that you know exactly what code is in the solution. This also gives you the opportunity to customize the solution exactly as you want it, to fit your needs. Most of the time a contact form does not have to be that advanced so a simple self-built solution is not that much effort.

A con of building an own solution is that it probably takes more time than installing a plugin for example, Wordpress. However, the plugins comes with the con that you don’t know exactly whats in them without really looking through the code. If the plugin is very popular there may also be bots trying to exploit weaknesses in the plugin code between releases. This happens a lot with Wordpress plugins. For smaller or less popular CMS this threat is not as common.

So, for my pages I’ve decided to built custom solutions. Which is what I will share in this tutorial. So let’s get started!

Demo project


This tutorial was made after creating the contact form at SlotCatalog.com. SlotCatalog is an catalog for online slots where you can see which games are the most popular games in different countries around the world.

We crawl over 800 casino sites worldwide and rank the slot games based on their position in the casino lobby. This makes it easy to navigate online slots, whenever it is old classics like Starburst, Dead or Alive, Jack and the Beanstalk or new popular releases like the Jammin Jars & Vikings slots.

What’s the crucial features of a Contact Form?

As stated previously, building a contact form in HTML5, CSS and PHP is not that much work. However, whenever you plan to build or implement a new feature you should always pause a while and think about what the need was that brought us here in the first place. With that need in mind, we have to define the core feature in general terms. When the core feature is defined we can break it down in smaller parts.

This leads us to two important questions:

  • What core features do we need?
  • What additional features that would be nice to have?

Defining the core feature

We need to enable an easy way for our visitors/users to contact us by mail regarding X, which have the opportunity to transform into two-way communication if we decide so.

Let’s start by taking an analogy too far. It would be nice to have access to all our customers thoughts about our product or service. Thoughts like “Jeesus that sign-up button was hard to find, that should really be placed to the right side of the form instead of in the bottom left.”.

We’re not saying that the specific user is always right. Many times these thoughts are preferences, and the collective preferences might differ from the specific user. However, the collective are often right.

Unfortunately, we can’t hear or see our customers thoughts. It’s actually a good thing in an age where privacy gets overran by giant tech companies. However, what we can do is making the effort for a customer to spell out their thoughts as small as possible. This leads us to receive as many improvement recommendations and other proposals as possible, which we may and may not listen to in the end. The important part is that they reach us, so we can make a decision on what value the recommendation or proposal has.

So, what do we actually need?

We’d like a good looking form where our user have to input the minimal amount of data necessary to make a contact request which should be able to turn into two-way communication. The easier it is for the user, the higher chance of them making an outreach to us about something that could be of importance to our business.

The contact form & It's input fields

To setup this form we would like their email (so we can respond) an the message. Including a subject as well seems like a small cost to give us an easier way of filtering our mailbox to see which requests are of interest.

Most contact forms online also includes a name field. In my own view this is not crucial data but it might be nice for the user to input their own names (people usually like themselves and love to see their own names) so why not. This also gives us the opportunity to respond in a more personal way.

There is a chance that you would like to include telephone number, while we will not add this in the tutorial itself we will go through fields and validations in general which will give you the knowledge on how to add additional fields.

The setup we’ve discussed above results in a form with the following fields:

  • Name
  • Email
  • Subject
  • Message

Now, Let’s talk two nice to have features – Form Validations & Spam Filter.

Nice to have: Form validations

There are two types of validations that are very nice to have in this scenario. The first is form validations. Form validations are nice to have because it helps our user with the input. In these situations I must admin that I usually think “How hard can it be to enter a four field form correctly?”. This thinking is not valid at all. Data shows that people are lazy, stressed, sleep-deprived etc which in turn leads people to make mistakes.

It would be sad if one of our biggest fans made a contact request about a great feature, which wouldn’t cost much to build and that we have not thought about ourselves. Now say he didn’t go into the details of the feature in his initial contact request and also forgot a dot in his email address. Not only did we miss the details about the feature in this situation, our biggest fan might also have gotten upset because we did not take the time to answer to his mail (because we couldn’t, since the email he entered was invalid).

Form validations will not help us to remove all user input errors, but it will help our user when they go to fast and miss something basic, like a dot in user@example.com.

Form validations can be done in several different ways (Front-end / Back-end), with different technologies. There’s javascript (and JS libraries / frameworks like jQuery, React), you can do the validations back-end as well, but since we don’t record our request to any database we have skipped this step in this tutorial. Instead, we have decided to use the HTML 5 Validations built into HTML 5.

Nice to have: Spam Filter

Spam Filter is another nice feature to have. If you aren’t familiar with the word its a simple validation that aims to differentiate human users from bots, thus removing a lot of spam contact request by bots that are crawling the internet and sending useless or malicious links through contact forms that they find online.

It is good practice is to implement several features to stop spam bots. There are loads of different methods. We have focused on CAPTCHA and the Honeypot method in this tutorial, which we will show how you easily implement.

It is worth to mention that Google developed an API service for filtering out bots called RECAPTCHA. You can sign up for the service for free and acquire a key to use the service. We won’t implement it in our tutorial though.


Kcaptcha is an mature CAPTCHA library for PHP which generates images with validation codes on them. The images are hard to read for bots while humans read them easily. The files used by kcaptcha is included in our download section. It is worth to mention that you need a PHP version above 4.0.6 (which we are sure you have in 2018) as well as the GD2 library support (most shared hosting services have this package installed), if you run your site from a VPS you can easily install the package with your distributions package manager.

Filter Spam with the Honeypot Technique

The honeypot spam filtering technique is simple to grasp. We simply create a field in the form that we hide to our real visitors with .css or javascript (in this tutorial we hide it with CSS). If this form is filled out we know it’s a bot that has been filling out the form because our real visitors didn’t even see the field.

Rolling our own Contact Form

In this tutorial we place the files inside a subdirectory called easycontactform. To better suite your site we have chosen this solution. You then include the easycontactform/index.php of the page where you want your contactform to be displayed. Like this:

contact.php (or the name of your contact page)

<?php include './easycontactform/index.php'; ?>

Building the contact form in HTML 5

The HTML tag for a form is simply as thought: form. The tag can receive several different attributes where action is an important one for us. The action attribute simply states what should handle a request. We have entered the name of our not yet built PHP script (the script that will handle the contact requests). The method attribute tells our visitors web browser what type of HTTP request to make (in this situation it is a POST request). If you want to read more about the different HTTP requests tutorialspoint.com have an excellent tutorial on it.

Inside the form tag we can define the input fields that we are interested in. There are several different form of input types. For a full reference we refer to W3Schools.com.

The input types that we are interested in when building our form is: Text, Email, Submit. In addition to this we are also interested in the seperate <textbox> tag.

HTML 5 Input Attributes


When reviewing the picture above and our previous knowledge of HTML we can decide on the input attributes. In this tutorial we have decided on a form with the following validations (feel free to change this based on your view):

Field name Input type Required Min Max Pattern
name text yes 3 40 -
m66 email no 8 100 [a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$
m77 email yes 8 100 [a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$
subject text yes 3 100 -
message textbox yes 10 - -

Actually, due to the honeypot implementation we will have two email field with the names “m66” and “m77”, where the m77 is the real email field and “m66” the fake one. Before we send the mail we will validate that “m66” is empty.

Our HTML Code

This allows us to write the following code:

<div class="contact-form-div">
  <div class="flash-div <?php if (isset($_SESSION['mail_status'])) echo ($_SESSION['mail_status'] ? 'mail-sent-true' : 'mail-sent-false'); ?> ">
    <?php if (isset($_SESSION['flash'])) { echo $_SESSION['flash']; unset($_SESSION['flash']); } ?>
  <form id="contact" method="POST">
    <input name="name" type="text" placeholder="Your Name*" required autofocus />
    <input id="m66" name="m66" type="email" placeholder="Your Valid Email*" pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$" />
    <input id="m77" name="m77" type="email" placeholder="Your Valid Email*" required pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$" />
    <input name="subject" type="text" placeholder="The Subject*" required />
    <textarea name="message" placeholder="Message Body*" rows="10" required></textarea>
    <div class="kcaptcha-div">
      <img src="./easycontactform/kcaptcha/?<?php echo session_name()?>=<?php echo session_id()?>">
        <a class="credits-link" href="https://easyphpcontactform.com" target="_blank">Credits: easyphpcontactform.com</a>
        <input type="text" name="keystring" placeholder="Image Code *" />
    <input type="submit" value="submit" />

Copy this code to easycontactform/index.php. If you don't have one yet, you create on or download the complete files at our download section.

Default styling looks bad, we need some CSS Styling!

The form does not look good with the default styling but we can easily fix this with some CSS styling. Copy & paste the CSS code below to your general stylesheet or save as contact.css

easycontactform/css/contact.css (or copy to your general stylesheet file)
/* Placeholder colors */
::-webkit-input-placeholder { /* WebKit, Blink, Edge */
    color:    #9b9b9b;
:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
   color:    #9b9b9b;
   opacity:  1;
::-moz-placeholder { /* Mozilla Firefox 19+ */
   color:    #9b9b9b;
   opacity:  1;
:-ms-input-placeholder { /* Internet Explorer 10-11 */
   color:    #9b9b9b;
::-ms-input-placeholder { /* Microsoft Edge */
   color:    #9b9b9b;

textarea::placeholder { color: #9b9b9b; font-family: arial; }

/* Colors for focused fields */

input[type=text], input[type=email], textarea {
  outline: none;
  border: 1px solid #9b9b9b;
input[type=text]:focus, input[type=email]:focus, textarea:focus {
  border: 1px solid #00C5BE;

/* Input styling */

textarea {
   font-family: arial;
   width: 27rem;
   font-size: 1rem;
   padding: 0.6rem;
   margin-right: 0.5rem;
   margin-left: 0.5rem;
   border-radius: 5px;
   border: 1px solid #9b9b9b;
   color: #9b9b9b !important;

.contact-form-div input {
   display: block;
   font-size: 1rem;
   width: 27rem;
   padding: 0.6rem;
   margin: 0.5rem;
   border-radius: 5px;
   border: 1px solid #9b9b9b;
   color: #9b9b9b !important;

.contact-form-div input[type=submit] {
   width: auto;
   background-color: #00C5BE;
   border: none;
   color: #fff !important;
   font-size: 1em;
   padding: 10px 50px;
   text-transform: uppercase;
   font-weight: normal;

/* Honeypot technique, hide the fake field */
#m66 {
   display: none;

If you saved the CSS as a seperate file you need to include the stylesheet on the contact page with the following html tag inside the <head> tag:

contact.php (or the name of your contact page)

  <link rel="stylesheet" href="easycontactform/css/contact.css">

Now that we have HTML and CSS markup done we can simply, as a visitor, submit the form which will talk to our server. Now we need to handle what our server should do with the data. Simply put, we need to check the spam validation and send the email if it passes. This we will do in PHP.

The very simple backend in PHP

This PHP script is not heavy, its a simple script that puts the parameters sent with the HTTP POST request into variables, perform a quick spam filter check (using kcaptcha & the honeypot technique) and then send our mail with the help of a SMTP (Simple Mail Transport Protocol) server. It also displays a quick flash message regarding the status of the mail. That means if it was sent or if there was any validation errors.

The PHP Part in start of file easycontactform/index.php


  //Session start, to generate dynamic kcaptcha codes and display flash messages

  //Display errors, can be removed when testing is done
  ini_set('display_errors', 1);
  ini_set('display_startup_errors', 1);


    //KCaptcha validation
    if(isset($_SESSION['captcha_keystring']) && $_SESSION['captcha_keystring'] === $_POST['keystring']) {
      //Variables from form, do not change unless adding/removing fields
      $name = $_POST['name'];
      $email = $_POST['m77'];
      $fakemail = $_POST['m66'];
      $subject = $_POST['subject'];
      $message = $_POST['message'];

      //Mail build
      $recipient = "your_mail@your_domain.com";
      $title = "Contact Form: $subject";
      $message_body = "Message: $message";
      $mailheader = "From: $email \r\n";
      $titles = 'From: contactform@your_domain.com' . "\r\n" .
          "Reply-To: " . $email . "\r\n" .
          'X-Mailer: PHP/' .phpversion();

      //Honeypot Technique
      if ($fakemail == "") {
        $_SESSION['flash'] = "Your message has been sent!";
        $_SESSION['mail_status'] = TRUE;
        mail($recipient, $title, $message_body, $titles) or die("Contact Mailer Error!");
      } else {
        $_SESSION['mail_status'] = FALSE;
        $_SESSION['flash'] = "Your message was not sent, we think you are a bot!";

      //Kcaptcha error message
      } else {
        $_SESSION['mail_status'] = FALSE;
        $_SESSION['flash'] = "Wrong CAPTCHA Code!";



<div class=”contact-form-div”>

Making sure that your php mail function works correctly

For the PHP mail function to work we need access to an SMTP (Simple Mail Transfer Protocol) service. This is basically a server that handles our request and sends the mail to the recipient (in this case our own email). There are external services to use and connect to this if you want to or we can use the one that comes with our hosting account / setup an own SMTP.

If you are running your site from a shared hosting solution / cPanel my guess is that your PHP mail function should work right out of the box with the hosting companies SMTP server.

From a VPS (Virtual Private Server) like a Digital Ocean droplet some configurations might be necessary to get it to work though. If that is the case you can either connect to an external SMTP server or read our quick guide on how to setup it yourself. This includes setting up postfix, mailutils and enabling the PHP mail function on Ubuntu 18.04. This might sound complicated but actually don’t take that much time and Digital Ocean have an excellent tutorial on the subject.