Still running FormMail.pl on your server? Here are solutions to every common error, plus a migration guide to modern alternatives.
Matt Wright’s FormMail was the web’s most popular CGI script, but in 2026 it’s also one of the most problematic. Hosting providers like xneelo have discontinued FormMail support entirely. Whether you need to fix a broken installation or migrate to something modern, this guide covers both paths.
This is the single most common FormMail error. When someone submits your form, FormMail checks whether the referring page’s domain is in its allowed list. If not, it blocks the request.
Bad Referer - Access DeniedThe form attempting to use this script resides atwww.yourdomain.com, which is not allowed to accessthis program.
The @referers array inside FormMail.pl does not include the domain that hosts your HTML form. FormMail compares the HTTP_REFERER header against this list and rejects anything that doesn’t match.
Open FormMail.pl in a text editor and find the @referers line near the top of the file. Add your domain, including both the bare domain and the www version:
# Before (default)
@referers = ('yourdomain.com');
# After (include www variant)
@referers = ('yourdomain.com', 'www.yourdomain.com');
If your site recently moved to HTTPS, the referer header now sends https://www.yourdomain.com instead of http://. Some older FormMail versions compare the full URL including protocol. The solution is either:
@referers array only contains domain names (not full URLs), which FormMail v1.6 should match correctlySome browsers send referer headers with trailing paths. Make sure you’re matching the domain, not a specific page URL. The @referers array should contain just the hostname:
# Wrong
@referers = ('www.yourdomain.com/contact.html');
# Correct
@referers = ('yourdomain.com', 'www.yourdomain.com');
@referers array. Wildcard entries like '*.yourdomain.com' are not supported in the original FormMail — you need to list each subdomain explicitly.
A 500 error means the server could not execute the script at all. The actual cause is always in the server error log. Check /var/log/httpd/error_log (Apache) or /var/log/nginx/error.log (Nginx) first. Below are the four most common reasons.
The first line of FormMail.pl (the shebang) tells the server where to find the Perl interpreter. If the path is wrong, the script cannot execute.
# Common paths — only ONE of these is correct for your server
#!/usr/bin/perl
#!/usr/local/bin/perl
#!/usr/bin/env perl
To find the correct path on your server:
$ which perl
/usr/bin/perl
Use whatever which perl returns as your shebang line. On most modern Linux servers, #!/usr/bin/perl is correct. On FreeBSD and some shared hosting, #!/usr/local/bin/perl is typical. The safest portable option is #!/usr/bin/env perl, which searches the system PATH.
If you edited FormMail on Windows and uploaded it via FTP in binary mode, the file will contain \r\n (CRLF) line endings instead of Unix \n (LF). The Perl interpreter will choke on the carriage return characters.
Fix: Re-upload using ASCII transfer mode in your FTP client, or convert line endings on the server:
# Convert CRLF to LF
$ dos2unix FormMail.pl
# Or with sed:
$ sed -i 's/\r$//' FormMail.pl
# Or with Perl itself:
$ perl -pi -e 's/\r\n/\n/g' FormMail.pl
CGI scripts must be executable by the web server. FormMail.pl needs 755 permissions (owner: read/write/execute, group and others: read/execute).
# Check current permissions
$ ls -la FormMail.pl
-rw-r--r-- 1 user group 12345 Jan 01 00:00 FormMail.pl
# Fix: make executable
$ chmod 755 FormMail.pl
# Verify
$ ls -la FormMail.pl
-rwxr-xr-x 1 user group 12345 Jan 01 00:00 FormMail.pl
On some shared hosting, the cgi-bin directory itself also needs 755 permissions, and the files inside must be owned by your user account (not root).
FormMail uses the sendmail command to deliver email. The $mailprog variable at the top of the script must point to a valid sendmail (or sendmail-compatible) binary:
# Default in FormMail.pl
$mailprog = '/usr/lib/sendmail';
# Common correct paths
$mailprog = '/usr/sbin/sendmail'; # Most modern Linux
$mailprog = '/usr/bin/sendmail'; # Some distributions
$mailprog = '/usr/local/bin/msmtp'; # If using msmtp
Find it on your server:
$ which sendmail
/usr/sbin/sendmail
If which sendmail returns nothing, sendmail is not installed. You’ll need to install it (yum install sendmail on RHEL/CentOS, apt install sendmail on Debian/Ubuntu) or switch to an SMTP-based alternative.
You submit the form and get a completely blank page — no error, no confirmation, nothing. This is almost always a missing or malformed HTTP content-type header.
CGI scripts must output a Content-type header before any HTML body. If the script crashes or exits before printing this header, the web server returns a blank page (or sometimes a 500 error, depending on the server configuration).
$ cd /var/www/yourdomain.com/cgi-bin/
$ perl FormMail.pl
Content-type: text/html
(If you see this header, the script starts correctly)
$ perl -c FormMail.pl
FormMail.pl syntax OK
If you see errors instead of “syntax OK,” fix the reported line numbers.
$ tail -20 /var/log/httpd/error_log | grep -i formmail
This error means the script died before outputting the required Content-type line.
print "Content-type: text/html\n\n"; line in FormMail.pl$success_url or $error_url variables and they point to pages that do not exist, FormMail may redirect to a blank or 404 pageCGI.pm or other modules, verify they are installed with perl -e "use CGI; print 'OK'"The form submits successfully, you may even see the “thank you” page, but the email never arrives. This is increasingly common as mail servers tighten spam filtering.
FormMail displays its confirmation page before checking whether sendmail actually delivered the message. The form appears to work, but the email silently fails. Verify $mailprog points to a valid binary (see Section 2, Cause 4).
Emails sent by FormMail via sendmail often lack proper authentication (no SPF, no DKIM, no DMARC). Modern email providers like Gmail, Outlook, and Yahoo will likely:
Fix: Check your spam/junk folder first. Then check the mail queue on the server:
# Check if mail is queued
$ mailq
# Check mail log for delivery status
$ tail -50 /var/log/maillog | grep yourdomain
# Test sendmail manually
$ echo "Subject: Test" | sendmail -v [email protected]
Some modern hosting environments (containers, minimal VPS images) do not ship with sendmail installed. If which sendmail returns nothing, you have three options:
yum install postfix or apt install postfixmsmtp that relays through an external SMTP server (Gmail, Mailgun, etc.)In NMS FormMail and some patched versions of the original, the @recipients or @allow_mail_to array restricts which email addresses FormMail is allowed to send to. If the recipient address in your form’s hidden recipient field is not in this list, the email is silently dropped.
# Check this array in FormMail.pl
@allow_mail_to = ('[email protected]', '[email protected]');
This is the most serious FormMail problem. If attackers discover your FormMail installation, they can use it to send spam to any email address — turning your server into an open relay.
FormMail.pl immediately. Every minute it stays accessible, your server is sending spam and your IP reputation is dropping.
Matt Wright’s FormMail v1.0–1.6 has two critical vulnerabilities:
%0ABcc: (a CRLF followed by a BCC header) into form fields like email or subject. FormMail passes this directly to sendmail, adding arbitrary recipients to every message.recipient hidden field can be set to any email address. Without proper validation, attackers submit the form with [email protected] and send email to anyone.Spammers routinely scan the web for /cgi-bin/FormMail.pl and /cgi-bin/formmail.pl using automated bots. Even if your FormMail has been sitting untouched for years, it can be discovered and exploited within days of being accessible.
mv FormMail.pl FormMail.pl.disabledmailq | head -50 — if there are thousands of queued messages, flush them: postsuper -d ALL$ grep -i "formmail" /var/log/httpd/access_log | tail -20
# Apache .htaccess
<Files "FormMail.pl">
Require all denied
</Files>
# Nginx
location ~* formmail\.pl$ {
deny all;
return 403;
}
If you need a Perl form handler, replace Matt Wright’s version with NMS FormMail (version 1.93 or later), which validates recipients against a whitelist and sanitizes all header fields. Better yet, migrate to a modern alternative entirely. See the full security history for technical details on each vulnerability.
An increasing number of hosting providers are actively removing FormMail from their servers and disabling legacy CGI support. This is not a bug — it is a deliberate security decision.
| Provider | Date | Details |
|---|---|---|
| xneelo (Hetzner South Africa) | November 30, 2025 | Announced end of FormMail support; recommended migrating to PHP or third-party form services |
| InMotion Hosting | 2024 | Removed legacy CGI scripts from new accounts; existing accounts can still use cgi-bin but without support |
| Pair Networks | 2023 | Deprecated Perl CGI in favor of PHP and Python-based solutions |
| Various cPanel Hosts | Ongoing | cPanel’s “CGI Center” no longer ships with FormMail since cPanel 100+ |
If your existing forms stopped working overnight, your host likely removed or blocked CGI execution. Check your hosting control panel for any announcements, or contact support. Do not try to “fix” a hosting-level restriction — migrate instead.
FormMail was revolutionary in 1995. In 2026, there are better options that are more secure, require less server configuration, and actually deliver emails reliably. Here are the most practical replacements, ranked by ease of migration.
Formspree is a form backend service. You change one line in your HTML (the action attribute) and your form works. No server-side code, no sendmail, no Perl. Free tier: 50 submissions per month.
<!-- Before: FormMail -->
<form action="/cgi-bin/FormMail.pl" method="POST">
<input type="hidden" name="recipient" value="[email protected]">
<input type="hidden" name="subject" value="Contact Form">
<input type="text" name="name" placeholder="Your Name">
<input type="email" name="email" placeholder="Your Email">
<textarea name="message"></textarea>
<button type="submit">Send</button>
</form>
<!-- After: Formspree (change action, remove hidden fields) -->
<form action="https://formspree.io/f/YOUR_FORM_ID" method="POST">
<input type="text" name="name" placeholder="Your Name">
<input type="email" name="email" placeholder="Your Email">
<textarea name="message"></textarea>
<button type="submit">Send</button>
</form>
Migration time: 5 minutes per form. Sign up, get your form ID, update the HTML.
Web3Forms provides a free form API. You include an access key as a hidden field and point the form action to their endpoint. No registration wall — just enter your email to get an API key.
<form action="https://api.web3forms.com/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
<input type="text" name="name" required>
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
Migration time: 5 minutes. Free tier: 250 submissions per month.
Basin is another form endpoint service with a generous free tier (100 submissions/month). It supports file uploads, spam filtering, and webhook integrations.
<form action="https://usebasin.com/f/YOUR_FORM_ID" method="POST">
<input type="text" name="name" required>
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
If your site is on Netlify, form handling is built in. Add netlify to the form tag and Netlify automatically collects submissions. Free tier: 100 submissions per month.
<form name="contact" method="POST" data-netlify="true">
<input type="text" name="name" required>
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
If you need server-side form handling and your host supports PHP (virtually all do), a simple PHP script replaces FormMail entirely:
<?php
// contact.php — minimal form handler
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$to = '[email protected]';
$subject = 'Contact Form: ' . htmlspecialchars($_POST['subject'] ?? 'No Subject');
$name = htmlspecialchars($_POST['name'] ?? '');
$email = filter_var($_POST['email'] ?? '', FILTER_VALIDATE_EMAIL);
$message = htmlspecialchars($_POST['message'] ?? '');
if ($email && $message) {
$body = "From: $name <$email>\n\n$message";
$headers = "From: $email\r\nReply-To: $email";
mail($to, $subject, $body, $headers);
header('Location: /thank-you.html');
exit;
}
}
header('Location: /contact.html?error=1');
?>
Point your form’s action to contact.php instead of FormMail.pl. This is the most direct server-side replacement.
If you specifically need a Perl CGI form handler (for example, other systems depend on FormMail’s output format), the NMS Project provides a secure drop-in replacement. NMS FormMail v1.93+ patches all known vulnerabilities while maintaining the same configuration interface.
# Download NMS FormMail
$ wget https://sourceforge.net/projects/nms-cgi/files/nms-formmail/
$ cp NMSFormMail-*.pl /var/www/yourdomain.com/cgi-bin/FormMail.pl
$ chmod 755 /var/www/yourdomain.com/cgi-bin/FormMail.pl
# Edit the configuration variables at the top of the file:
# - Set @allow_mail_to to your email addresses
# - Set @referers to your domains
# - Set $mailprog to your sendmail path
| Solution | Cost | Server Required | Migration Time | Spam Protection |
|---|---|---|---|---|
| Formspree | Free (50/mo) | No | 5 min | Built-in |
| Web3Forms | Free (250/mo) | No | 5 min | Built-in |
| Basin | Free (100/mo) | No | 5 min | Built-in |
| Netlify Forms | Free (100/mo) | Netlify only | 2 min | Built-in |
| PHP mail() | Free | Yes (PHP) | 15 min | Manual (add reCAPTCHA) |
| NMS FormMail | Free | Yes (Perl) | 10 min | Basic validation |
The “Bad Referer” error means the domain hosting your HTML form is not listed in the @referers array inside FormMail.pl. Open the script, find the @referers line, and add your domain — both with and without www. Also verify whether your site uses HTTP or HTTPS, as the referer header includes the protocol. See Section 1 for detailed steps.
A 500 error in FormMail has four common causes: wrong Perl interpreter path in the shebang line, Windows CRLF line endings, incorrect file permissions (needs chmod 755), or a missing sendmail binary. Check your server’s error log first — it will tell you the exact cause. See Section 2 for fixes for each scenario.
No. Matt Wright’s original FormMail.pl (versions 1.0 through 1.6) has known, actively exploited vulnerabilities: CVE-2001-0357 (email injection) and CVE-2002-0564 (open relay). Spammer bots still scan for FormMail installations daily. If you must use a Perl form handler, use NMS FormMail v1.93 or later. For most use cases, a modern service like Formspree or Web3Forms is a safer and easier choice. Read the full security history for details.
Several categories of tools have replaced FormMail:
mail(), Python Flask/Django form handlers, Node.js with NodemailerSee the Migration Guide above for code examples and a comparison table.
The migration takes about five minutes per form:
<form action="..."> from the CGI path to your Formspree URLrecipient, subject, redirect, etc.) — Formspree handles these through its dashboard settings<input> and <textarea> has a name attribute — Formspree uses these as field labels in the emailSee the before/after code example above for the exact HTML changes.
Run through this checklist when debugging any FormMail issue. Each step takes 30 seconds or less.
| # | Check | Command | Expected Result |
|---|---|---|---|
| 1 | Perl is installed | which perl |
/usr/bin/perl |
| 2 | Shebang matches | head -1 FormMail.pl |
Matches which perl output |
| 3 | Permissions are 755 | ls -la FormMail.pl |
-rwxr-xr-x |
| 4 | No syntax errors | perl -c FormMail.pl |
syntax OK |
| 5 | Unix line endings | file FormMail.pl |
No “CRLF” in output |
| 6 | Sendmail exists | which sendmail |
/usr/sbin/sendmail |
| 7 | @referers includes your domain | grep '@referers' FormMail.pl |
Your domain is listed |
| 8 | $mailprog path is correct | grep '$mailprog' FormMail.pl |
Matches which sendmail |
| 9 | CGI execution is enabled | curl -I https://yourdomain.com/cgi-bin/ |
Not 403 Forbidden |
| 10 | Error log shows the cause | tail -20 /var/log/httpd/error_log |
Check for Perl/CGI errors |
Complete documentation for Matt Wright’s FormMail v1.6: configuration variables, form fields, installation guide, and download.
How FormMail became the internet’s biggest spam tool: CVE-2001-0357, CVE-2002-0564, and the NMS Project response.
Download every script from Matt’s Script Archive in one package, including FormMail, Guestbook, WWWBoard, and more.