Powering a website form with Formmail

Formmail is an open source web server CGI script that captures and processes form content and mails it to a recipient. One common use is to power a “Contact Us” form on a website, allowing visitors to contact the site owner without having to use a mail client and without revealing the owner’s email address (for example, to spammers’ mail harvest bots).

There are many versions of Formmail available, some of which have serious security issues. For this reason Formmail has a bad reputation in certain circles, but this is undeserved: you need merely use a secure version. Reputable web hosts will supply their own secure version, though of course you will want to check out the source code yourself. If you are your own host or are setting up a local development box, nms-Formmail is widely regarded as best of breed.

Formmail is not your only choice. I prefer the easier to configure cgiemail, and no doubt there are other alternatives. However, Formmail is perhaps the most popular and documented solution, and it certainly was the best supported one for the particular task I faced: adding recapcha validation to a form at my web host. Thus the following describes setting up a web-host-provided version of Formmail to power a web form. Once working, we will then add captcha validation to minimize form abuse.

Naturally, before beginning this or any such job, back up your site and secure the backup offsite. If this is your first time playing with CGI scripts, consider trying this on a local development box before doing it on a production server.

All HTML examples validate as 4.01 Transitional.

Begin, optionally, by copying your web host’s version of Formmail to your Apache docroot. If you don’t plan to customize Formmail’s code you don’t have to (and shouldn’t) do this, but — as we shall see — using recaptcha or making a “send to a friend” form will require this.

Your filenames and paths will vary (consult your web host), but in my case the relevant command using ssh was:

cp /usr/www/cgi-bin/recaptcha_formmail.pl public_html/

Set the file’s permissions to the lowest necessary to work; on my web host that is 755. While you’re doing this, you might as well change the name of the file to something else. Security through obscurity is not to be relied on, but it’s easy to do and it makes the bad guys’ job a little harder.

Whether you copy Formmail to your docroot or not, do make a note of the name and full path to the instance of Formmail you will be using.

For clarity I shall illustrate this section with new, static HTML pages, though of course the same principles apply to adding code to existing static or dynamically generated pages.

First you need a form page. A basic form page would have code such as:

<form method="post" action="path/to/formmail.pl">
 <input type="hidden" name="recipient"
 <input type="hidden" name="subject" value="Message Subject">
 <input type="hidden" name="env_report" value="REMOTE_HOST">
 <input type="hidden" name="missing_fields_redirect"
 <input type="hidden" name="redirect"
 Your name (optional):<br>
 <input type="text" name="realname"><br><br>
 Your email (required):<br>
 <input type="text" name="email"><br><br>
 Your question (required):<br>
 <textarea name="message" cols="35" rows="4">
 <input type="hidden" name="required" value="email,message">
 <input type="submit" value="Send your question">

Much of the code in the example file is self-explanatory, but let’s consider some of it more closely:

  • path/to/formmail.pl may be expressed as either the local path or a URL.
  • For testing purposes, the value of recipient should be your own email address, so that you will receive all form submissions. Naturally this will have to be changed to the real recipient’s email address once the form is placed in production. Multiple recipients can be specified, separated by a comma.
  • The value env_report can take any number of variables; REMOTE_HOST just happens to be the only one I’m interested in.
  • The two redirect values must be expressed as absolute URLs. The first, missing_fields_redirect, gives the URL of your fail page, and the second, redirect, is the URL of your success page (both of which we will create in a moment).
  • Next are examples of data input fields; here three typical examples are shown.
  • The value required is a list of those input fields that are required and, if left blank, will result in the form not being sent.

You might find this example and its source code to be helpful (although the HTML does not validate; I have corrected that in my example above).

Many other fields are available; for simplicity’s sake only the most common are given here.

The above assumes that you want the form to be sent to a particular recipient. Sometimes, however, you want the site visitor to designate the recipient, for example on a “send to a friend” form. Secure versions of Formmail will not allow this by default, as it lends itself to abuse. You can, however, modify formmail.pl to allow this. On my web host’s version of formmail.pl, I found and commented out this line:

# Check Recipient of mail

Now in your form, find this line:

<input type="hidden" name="recipient" 

…and change it to something like:

Recipient's email:
  <input name="recipient">

Your form will now have a new field for the recipient’s email, and formmail.pl will no longer reject recipients outside of your hosting account’s domain. If you do this, be a good net citizen and minimize the chance of abuse: require recaptcha or other user validation, and do not provide the user with fields in which spam can be placed.

Next we need fail and success pages. Formmail can send the visitor to your custom fail page if a required field was left blank, or to your custom success page if the form was successfully accepted and delivered. These custom pages are not strictly required, as Formmail has similar functionality built-in. For usability reasons, however, fail and success pages, properly integrated into your site’s design, are preferred.

The URLs of your custom fail and success pages are coded into the form as shown above. What those pages contain should be whatever your particular visitors need to clearly understand that the form was or was not sent, and in the latter case, what to do about it. On one recent job I used the following text on the fail page:

Additional Information Required
You did not provide enough information to allow us to answer your question. Please go back, using the “back” button on your browser, and provide at least:

  • Here goes a bullet list of the form’s required fields, one by one
  • And so on

And this on the success page:

Question Received
Thank you! We have received your question and you may expect a personal reply shortly. You may continue browsing our site.

Upload your three pages (form, fail, and success) and test them. Things to test for are properly enforcing required fields, accepting forms with optional fields left blank, the proper display of fail and success pages, and the proper receipt of submitted forms.

At this point your form is working, and if it is for purely local use then your job is finished. For an Internet-facing form, however, you will likely want a captcha solution to avoid being buried in spam. In this example I shall demonstrate how to incorporate recaptcha, one popular solution.

First, sign up for a free recaptcha account. Enter the domain name you wish to use. By default, your recaptcha key is restricted to one specified domain for additional security. If you wish to use recaptcha on multiple domain names you can sign up for multiple keys (more secure), or opt to create a global key that works on all domains (less secure but needed in some circumstances). When you have finished the signup process, write down the public and private keys you are issued.

Next, place your private key in Formmail. The version of Formmail my web host supplies is recaptcha-enabled, so in my case I added my recaptcha private key here:

## RECAPTCHA - user must configure their private key here
my $PRIVATE_KEY = '<your_private_key>';

My web host’s version of Formmail shows its own generic fail page if the recaptcha challenge fails. I don’t like that and prefer visitors to see my site’s custom fail page, so I replaced this code in formmail.pl:

if (($error eq 'missing_fields') &&
    ($CONFIG{'missing_fields_redirect'} =~ m#https?://.+..+#)) {

…with this (code courtesy Sequoia Consulting):

if ( ( ($error eq 'missing_fields')
       || ($error eq 'bad_recaptcha_response') ) &&
      ($CONFIG{'missing_fields_redirect'} =~ m#https?://.+..+#)
    ) {

After that, add your public key and other recaptcha code to your form. In your form, find the submission button line, which will look something like:

<input type="submit" value="Send your question">

And insert before it this code:

<script type="text/<span class=">// <![CDATA[
// <![<span class="hiddenSpellError" pre="">CDATA</span>[
// ]]></script>javascript</span>">
// ]]>var RecaptchaOptions = {
theme : ‘red’,
lang : ‘en’
<script type="text/<span class=">// <![CDATA[
// ]]></script>

<textarea name="recaptcha_challenge_field" rows="3" cols="40">
<input type="hidden" name="recaptcha_response_field"

Let’s consider some of this code more closely:

  • The first five lines are optional and set the recaptcha widget’s theme and language. Shown here are the default settings of a red themed widget in English; see here for available parameters.
  • Replace both instances of <your_public_key> with your public key.

Finally, revise your fail page, adding an additional bullet along the lines of “The two words you see in the box, in order and separated by a space”.

Now upload your revised Formmail code and the form and fail pages. Test for correct, incorrect, and empty captcha challenge responses. If all goes well, you are done.

Original Formmail documentation at Matt’s Script Archive
Unofficial Guide to FormMail.pl
reCAPTCHA Client API Documentation


About Warren Post

So far: Customer support guy, jungle guide, IT consultant, beach bum, entrepreneur, teacher, diplomat, over-enthusiastic cyclist. Tomorrow: who knows?
This entry was posted in Uncategorized and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s