Form Handlers and Mailers

CGI form handlers: how contact forms and email mailers work server-side

How does a CGI form handler send contact form submissions by email?

A CGI form handler is a server-side script, typically written in Perl or Python, that receives POST data from an HTML form, validates and sanitizes the input, and then calls the server's mail transfer agent (sendmail or an SMTP library) to send the submission to a configured address. The script runs on the web server rather than in the browser, so the recipient email address is never exposed in the page source.

Request custom development Getting started

What a CGI form handler actually does

When a visitor submits an HTML form with method='POST' and an action attribute pointing to a CGI script, the web server passes the form data to that script through environment variables and standard input. The script reads and decodes the data (key=value pairs, URL-encoded), validates each field, and decides what to do next, typically sending an email and returning a thank-you page.

The mail step is where the choice matters. On older shared hosts the script calls sendmail directly by opening a pipe to /usr/sbin/sendmail. On modern setups it is cleaner to use an SMTP library (Net::SMTP in Perl, smtplib in Python) and authenticate with your mail service's credentials, which gives you proper delivery receipts and avoids being treated as a spam source by the receiving server.

The minimum a form handler must do

At bare minimum, every form handler must read the POST body, URL-decode each field, strip or escape HTML special characters before echoing anything back to the browser, validate that required fields are present, and validate that email addresses look like email addresses before passing them to any mail function. Skipping any of these steps opens the door to cross-site scripting (echoing raw user input) or email header injection (appending extra To/Cc lines via a crafted Name field).

Header injection is the most commonly overlooked risk. If you build the email headers by concatenating user input directly, an attacker can stuff newlines into the Name field and add their own recipients, turning your contact form into a spam relay. The fix is to strip newline characters from every field you put into email headers and to use a mail library that handles header construction for you rather than raw string concatenation.

Spam prevention without third-party scripts

Three techniques work well without loading any external JavaScript. First, a honeypot field: add a hidden text input with a name bots expect to fill (like 'website' or 'url'), keep it invisible with CSS, and reject any submission where that field is non-empty. Bots fill every visible and hidden field; real humans leave hidden ones blank.

Second, a time gate: record the time the form page was loaded (in a hidden field or server-side session), and reject submissions that arrive faster than a human could read and fill the form, typically under three to five seconds. Third, for high-volume forms, a server-side CAPTCHA or math challenge avoids the JavaScript dependency of reCAPTCHA while still filtering most automated submissions.

Choosing between Perl, Python, and CGI.pm

Perl with CGI.pm was the dominant choice for contact mailers throughout the 1990s and 2000s and still runs on nearly every shared host that supports CGI. CGI.pm handles parameter parsing, header generation, and HTML output escaping for you. Its form-handling code is terse and well-understood by a generation of tutorials. The downside is that CGI.pm is no longer maintained as aggressively, and Perl's CPAN dependency chain can be hard to manage on shared hosts where you cannot install modules.

Python's cgi module (standard library) gives you a similar level of convenience, and smtplib plus email.mime make the mail step clean. Python 3 is the current version; be aware that some older shared hosts still default to Python 2 in the CGI path, so confirm the interpreter version before writing Python 3 syntax. For either language, the actual form-handler code is small, typically fifty to a hundred lines for a complete, validated, spam-resistant contact mailer.

When a hosted form service makes more sense

CGI form handlers are worth self-hosting when you control the server, need the submission data in a specific format, or want to avoid third-party data processors entirely. They become a liability when your host restricts outbound SMTP, when sendmail is not available, or when you cannot keep the Perl or Python interpreter version up to date. In those cases a hosted form service (Formspree, Netlify Forms, or a simple AWS SES endpoint) offloads the mail delivery problem and handles spam filtering, while your HTML form simply points at their endpoint. The trade-off is that submission data passes through a third party.

The right choice depends on your hosting environment and your tolerance for maintenance. If you are on a modern VPS where you can install any package, self-hosted CGI gives you full control. If you are on a locked-down shared host where you cannot install CPAN modules or configure SMTP credentials, a hosted form endpoint is more reliable and takes less ongoing upkeep.

What to know

Key points

Get help

Listings and a local agent, when you are ready

Need a CGI script built, debugged, or a hosting environment configured? Forms use a clearly-marked placeholder endpoint until wired to a real system.

Hosting partner CGI-compatible hosting for contact forms

Reserved for an affiliate link to a vetted shared or VPS hosting provider that supports CGI and Perl/Python execution. Operator wires affiliate link here.

Development Need a custom CGI script built?

Self-hosted lead form for custom CGI development inquiries. Placeholder endpoint until wired to the operator's CRM or email handler.

Open inquiry form →

Custom CGI development

No spam. Your inquiry is answered within 1 business day.

Hosting setup help

No spam. Your inquiry is answered within 1 business day.

Questions

Frequently asked questions

How does a CGI contact form work?
An HTML form with method='POST' points its action attribute at a CGI script on the server. When submitted, the server passes the form fields to the script through the environment and standard input. The script reads, decodes, validates, and sanitizes the input, then calls a mail function to send the data to a configured address, and returns a response page to the browser.
What is email header injection and how do I prevent it?
Header injection happens when an attacker puts newline characters into a form field that ends up in an email header, letting them append their own To, Cc, or Subject lines and use your form as a spam relay. The fix is to strip all newline characters (\n and \r) from any field you place in email headers, and to use a mail library that constructs headers safely rather than building them with string concatenation.
Does my CGI form handler work if my host blocks outbound SMTP?
If your host blocks port 25 or restricts SMTP, the form handler cannot deliver mail directly. Options are: use your host's sendmail binary if available, configure authenticated SMTP on a permitted port (587 or 465), use a transactional mail API (Amazon SES, Mailgun, Postmark) via their HTTP API instead of SMTP, or switch to a hosted form endpoint service that handles delivery for you.
What is a honeypot field and does it work?
A honeypot is a form field hidden from humans via CSS (not the hidden type, since bots know to ignore those) that bots tend to fill in automatically. The server-side script rejects any submission where the honeypot field is non-empty. It is not foolproof against sophisticated bots, but combined with a time gate it stops the majority of automated form spam without requiring JavaScript or a CAPTCHA.
CGI.pm vs Python cgi module: which should I use?
Both handle parameter parsing cleanly. CGI.pm (Perl) is the traditional choice and runs on virtually every CGI-capable shared host. Python's cgi module is a clean alternative if your host has Python 3 in the CGI path. CGI.pm is no longer actively maintained, but for a simple contact mailer it is stable. Use whichever language you are more comfortable maintaining, since you will need to update it if a vulnerability is found.
Can I use a CGI form handler on shared hosting?
Yes, provided your shared host enables CGI execution and has Perl or Python installed, which most traditional shared hosts do. The script needs execute permissions (typically chmod 755) and must be in the cgi-bin directory or a location your host designates as CGI-enabled. Confirm the exact requirements with your host, since some modern shared hosts serve only static files or PHP and have removed CGI support.

CustomCGI publishes reference information on CGI programming and server-side web scripting for educational and informational purposes. Code examples are provided as-is for learning; always audit and test scripts in a staging environment before deploying to production. Security vulnerabilities in CGI scripts are a real risk; follow current best practices and review the security guide before deploying any user-facing code.