Guestbook

Allow visitors to sign your guestbook and leave comments on your website. One of the most popular CGI scripts from the early web era.

Perl v2.3.1 Legacy

Quick Info

  • Version: 2.3.1
  • Released: October 29, 1995
  • Language: Perl 5+
  • License: Free for use
  • Files: 5 (guestbook.pl, addguest.html, guestbook.html, guestlog.html, README)

Historical Context

Guestbook was one of the original free CGI scripts from the 1990s, created in 1995. It became the gold standard for website guestbooks, with millions of installations across the early web.

"In the 1990s, every personal website had a guestbook. It was a way to prove your site had visitors and build a sense of community before social media existed."

Why Guestbooks Mattered

  • Social proof: Showed site had real visitors
  • Community: Created connection before social media
  • Feedback: Let visitors communicate with webmaster
  • Fun: Leaving your mark on someone's site

The Decline of Guestbooks

  • Rise of social media (2004+)
  • Blog comments became standard
  • Spam overwhelmed unmoderated guestbooks
  • Facebook/Twitter became the new "signing" places

Modern Comment Systems (2024)

Today's comment systems offer spam protection, moderation, and social features the original guestbook couldn't provide:

GitHub-Based (Free, Developer Audience)

Giscus

Uses GitHub Discussions. Supports replies, reactions, moderation. No rate limits, self-hosted option.

Free GitHub Discussions

Utterances

Uses GitHub Issues. Lightweight, privacy-focused, easy setup. Great for tech blogs.

Free GitHub Issues

Self-Hosted (Full Control)

Isso

Python + SQLite, 40KB JS. Import from Disqus/WordPress. Markdown support, moderation.

Free Open Source

Remark42

Social login (Google, Facebook, GitHub). Notifications via Slack/Telegram. Docker deploy.

Free Go

Comentario

Successor to Commento. 20KB JS, voting, Markdown. Docker-ready.

Free Open Source

Third-Party Services (No Server Required)

Disqus

Most popular. Free tier has ads. Full moderation, social login.

Free tier Ads

FastComments

Fast, simple, privacy-focused. No ads. From $5/mo.

From $5/mo No ads

Cusdis

Lightweight (5KB), privacy-first. Free self-hosted, paid cloud.

Free self-host Cloud $5/mo

Comparison: Guestbook vs Modern Comments

Feature 1995 Guestbook Modern Systems
Spam protectionNoneAI/CAPTCHA
ModerationManual edit filesAdmin panel
Replies/threadingNoYes
Social loginNoGoogle, GitHub, etc.
NotificationsEmail onlyEmail, Slack, Telegram
Reactions/votingNoYes
Mobile-friendlyNoResponsive
Server requiredYesOptional

Overview

Guestbook allows you to set up your own comments page where visitors can leave entries. Entries are displayed with the most recent at the top (or bottom, configurable) and scroll down the page.

Key capabilities include:

  • Limit or allow HTML in entries
  • Link email addresses with mailto tags
  • Use a log to track entries
  • Redirect to a different page after signing
  • Email notification when new entries are added
  • Auto-reply to visitors who sign
  • Customizable entry separators
  • Line break preservation option

Package Contents

File Description
README Installation instructions and configuration guide
guestbook.pl The main Perl script that processes entries
guestbook.html The guestbook display page with entries
addguest.html The form for visitors to fill out
guestlog.html Short log of guestbook entries (optional)

Features by Version

  • Line Breaks Option: Convert line breaks in comments to <br> tags
  • Improved error handling with die routines
  • Cleaned up references to removed variables

  • HTML Control: Option to allow or disallow HTML tags in entries
  • URL Field: Visitors can add their website URL
  • Security Fix: Fixed Server Side Includes vulnerability

  • Entry Order: Choose newest-first or oldest-first display
  • Remote Mail: Auto-send thank you email to visitors
  • Bug fixes for mail and display options

  • Email Notification: Get notified when entries are added
  • Log Option: Enable/disable entry logging
  • Link Mail: Make email addresses clickable
  • Separator: Choose between <hr> or <p>
  • Redirection: Auto-redirect or show confirmation page

Installation

Step 1: Download and Extract

Download the guestbook package and extract it to your local machine.

Step 2: Upload Files

Upload the files to your server:

  • guestbook.pl → your CGI-bin directory
  • guestbook.html → your web directory
  • addguest.html → your web directory
  • guestlog.html → your web directory (if using logging)

Step 3: Set Permissions

# Make the script executable
chmod 755 guestbook.pl

# Make HTML files writable by the server
chmod 666 guestbook.html
chmod 666 guestlog.html

Step 4: Configure the Script

Edit guestbook.pl and set the required variables (see Configuration section below).

Step 5: Update Form Action

Edit addguest.html and update the form action to point to your guestbook.pl location:

<form method="POST" action="/cgi-bin/guestbook.pl">

Configuration Options

Required Variables

Variable Description Example
$guestbookurl URL address of your guestbook.html file "http://example.com/guestbook.html"
$guestbookreal System path to guestbook.html "/home/user/public_html/guestbook.html"
$guestlog System path to guestlog.html "/home/user/public_html/guestlog.html"
$cgiurl URL to the guestbook.pl script "http://example.com/cgi-bin/guestbook.pl"

Optional Variables (0 = off, 1 = on)

Variable Default Description
$mail 0 Email notification when entry is added
$uselog 1 Use the short log feature
$linkmail 1 Make email addresses clickable
$separator 0 Use <hr> (1) or <p> (0) between entries
$redirection 1 Auto-redirect to guestbook after signing
$entry_order 1 Newest first (1) or oldest first (0)
$remote_mail 0 Send thank you email to visitor
$allow_html 0 Allow HTML tags in entries
$line_breaks 0 Convert line breaks to <br>

Mail Variables (required if $mail or $remote_mail = 1)

Variable Description Example
$recipient Your email address "[email protected]"
$mailprog Path to sendmail "/usr/sbin/sendmail"

Code Examples

Modern implementations of guestbook functionality in different languages:

#!/usr/bin/perl
use strict;
use warnings;
use CGI;
use HTML::Entities;

my $cgi = CGI->new;

# Configuration
my $guestbook_file = '/path/to/guestbook.html';
my $max_entries = 100;

# Get form data
my $name = $cgi->param('name') || '';
my $email = $cgi->param('email') || '';
my $url = $cgi->param('url') || '';
my $city = $cgi->param('city') || '';
my $country = $cgi->param('country') || '';
my $comments = $cgi->param('comments') || '';

# Sanitize input
$name = encode_entities($name);
$email = encode_entities($email);
$url = encode_entities($url);
$city = encode_entities($city);
$country = encode_entities($country);
$comments = encode_entities($comments);

# Validate required fields
if (!$name || !$comments) {
    print $cgi->header();
    print "<h1>Error</h1><p>Name and comments are required.</p>";
    exit;
}

# Create entry
my $date = localtime();
my $entry = qq{
<div class="guestbook-entry">
    <strong>$name</strong>};

$entry .= " <$email>" if $email;
$entry .= qq{<br>
    <em>$city, $country</em><br>
    <p>$comments</p>
    <small>$date</small>
</div>
<hr>
<!--begin-->
};

# Read existing guestbook
open(my $fh, '<', $guestbook_file) or die "Cannot open: $!";
my $content = do { local $/; <$fh> };
close($fh);

# Insert new entry after <!--begin-->
$content =~ s/<!--begin-->/$entry/;

# Write updated guestbook
open($fh, '>', $guestbook_file) or die "Cannot write: $!";
print $fh $content;
close($fh);

# Redirect to guestbook
print $cgi->redirect($guestbook_file);
<?php
// Modern PHP Guestbook Implementation

// Configuration
$guestbook_file = 'guestbook_entries.json';
$max_entries = 100;

// Get form data
$name = htmlspecialchars($_POST['name'] ?? '', ENT_QUOTES, 'UTF-8');
$email = htmlspecialchars($_POST['email'] ?? '', ENT_QUOTES, 'UTF-8');
$url = htmlspecialchars($_POST['url'] ?? '', ENT_QUOTES, 'UTF-8');
$city = htmlspecialchars($_POST['city'] ?? '', ENT_QUOTES, 'UTF-8');
$country = htmlspecialchars($_POST['country'] ?? '', ENT_QUOTES, 'UTF-8');
$comments = htmlspecialchars($_POST['comments'] ?? '', ENT_QUOTES, 'UTF-8');

// Validate required fields
if (empty($name) || empty($comments)) {
    die('<h1>Error</h1><p>Name and comments are required.</p>');
}

// Load existing entries
$entries = [];
if (file_exists($guestbook_file)) {
    $entries = json_decode(file_get_contents($guestbook_file), true) ?: [];
}

// Add new entry
$new_entry = [
    'name' => $name,
    'email' => $email,
    'url' => $url,
    'city' => $city,
    'country' => $country,
    'comments' => $comments,
    'date' => date('Y-m-d H:i:s'),
    'ip' => $_SERVER['REMOTE_ADDR']
];

// Add to beginning (newest first)
array_unshift($entries, $new_entry);

// Limit entries
$entries = array_slice($entries, 0, $max_entries);

// Save entries
file_put_contents($guestbook_file, json_encode($entries, JSON_PRETTY_PRINT));

// Redirect to guestbook
header('Location: guestbook.php');
exit;
// Modern JavaScript Guestbook (Node.js/Express)
const express = require('express');
const fs = require('fs').promises;
const sanitizeHtml = require('sanitize-html');

const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

const GUESTBOOK_FILE = './guestbook.json';
const MAX_ENTRIES = 100;

// Load entries
async function loadEntries() {
    try {
        const data = await fs.readFile(GUESTBOOK_FILE, 'utf8');
        return JSON.parse(data);
    } catch {
        return [];
    }
}

// Save entries
async function saveEntries(entries) {
    await fs.writeFile(GUESTBOOK_FILE, JSON.stringify(entries, null, 2));
}

// Add entry endpoint
app.post('/guestbook/sign', async (req, res) => {
    const { name, email, url, city, country, comments } = req.body;

    // Validate required fields
    if (!name || !comments) {
        return res.status(400).json({ error: 'Name and comments required' });
    }

    // Sanitize input
    const entry = {
        name: sanitizeHtml(name),
        email: sanitizeHtml(email || ''),
        url: sanitizeHtml(url || ''),
        city: sanitizeHtml(city || ''),
        country: sanitizeHtml(country || ''),
        comments: sanitizeHtml(comments),
        date: new Date().toISOString(),
        ip: req.ip
    };

    // Load, add, and save
    const entries = await loadEntries();
    entries.unshift(entry);
    const trimmed = entries.slice(0, MAX_ENTRIES);
    await saveEntries(trimmed);

    res.redirect('/guestbook');
});

// Display guestbook
app.get('/guestbook', async (req, res) => {
    const entries = await loadEntries();
    res.render('guestbook', { entries });
});

app.listen(3000);

HTML Form Template

<!-- addguest.html - Guestbook Entry Form -->
<form method="POST" action="/cgi-bin/guestbook.pl">
    <div class="mb-3">
        <label for="name" class="form-label">Name *</label>
        <input type="text" class="form-control" id="name" name="name" required>
    </div>

    <div class="mb-3">
        <label for="email" class="form-label">Email</label>
        <input type="email" class="form-control" id="email" name="email">
    </div>

    <div class="mb-3">
        <label for="url" class="form-label">Website URL</label>
        <input type="url" class="form-control" id="url" name="url" placeholder="https://">
    </div>

    <div class="row">
        <div class="col-md-6 mb-3">
            <label for="city" class="form-label">City</label>
            <input type="text" class="form-control" id="city" name="city">
        </div>
        <div class="col-md-6 mb-3">
            <label for="country" class="form-label">Country</label>
            <input type="text" class="form-control" id="country" name="country">
        </div>
    </div>

    <div class="mb-3">
        <label for="comments" class="form-label">Comments *</label>
        <textarea class="form-control" id="comments" name="comments" rows="5" required></textarea>
    </div>

    <button type="submit" class="btn btn-primary">Sign Guestbook</button>
</form>

Download

Download the Guestbook script package in your preferred format:

View Individual Files

Working Demo

See the Guestbook script in action:

More examples can be found in the "In Action" section below.

Frequently Asked Questions

This is usually a permissions issue. Make sure:

  1. The guestbook.html file has write permissions: chmod 666 guestbook.html
  2. The $guestbookreal variable points to the correct system path
  3. The directory containing guestbook.html is writable by the web server
  4. Check your server error logs for more details

Mail issues are common. Check the following:

  1. Verify the $mailprog path is correct (usually /usr/sbin/sendmail or /usr/lib/sendmail)
  2. Make sure $recipient contains a valid email address
  3. Your hosting provider may have restrictions on sendmail - contact them
  4. Some shared hosts require SMTP instead of sendmail

The date is generated by Perl's localtime() function. If it's not appearing:

  • Check that entries are being written at all (try adding a test entry)
  • The server's timezone settings may affect the displayed time
  • Review the entry format in guestbook.pl to ensure date is included

While the original third-party guestbook hosting services are mostly gone, modern alternatives include:

  • Self-hosting on any web server with Perl/PHP support
  • Using cloud platforms like Heroku, Railway, or Vercel
  • Embedding services like Disqus or Facebook Comments
  • Static site solutions using Netlify Forms or Formspree

Spam prevention wasn't built into the original script. Modern approaches include:

  • Adding CAPTCHA (reCAPTCHA, hCaptcha)
  • Honeypot fields (hidden fields that bots fill out)
  • Rate limiting by IP address
  • Content filtering for known spam patterns
  • Requiring email verification
  • Manual moderation before entries appear

The original 1995 script has known security vulnerabilities, including:

  • Server Side Includes (SSI) injection (fixed in v2.3)
  • Limited input validation
  • No CSRF protection
  • No spam prevention

For production use, we recommend using our modern implementations that include proper input sanitization, CSRF tokens, and spam protection.

Yes! You can customize entries by:

  • Editing the HTML output section in guestbook.pl
  • Modifying guestbook.html template
  • Adding CSS styles to your page
  • Changing the $separator option (hr vs p)

Modern implementations often use JSON storage with template engines for even more flexibility.

The original script doesn't have an admin interface. To manage entries:

  • Edit guestbook.html directly via FTP/SSH
  • Delete entries by removing the HTML between entry dividers
  • Consider adding an admin panel in a modern implementation

View All FAQs | Guestbook FAQ Archive

Guestbook in Action

See examples of Guestbook implementations around the web:

These are historical examples showing how the script was used on real websites.

Related Scripts

WWWBoard Perl

Discussion board and message forum - great for more interactive community features.

FormMail Perl

Email form handler - if you just need to receive form submissions via email.

Counter Perl

Visitor counter - track how many people visit your guestbook.

Book 'em Dan-O Perl

Visitor logging script - track visitors without a visible guestbook.

Version History

v2.3.1 Current October 29, 1995

  • Added line_breaks option for comment formatting
  • Added die routines for better error handling
  • Removed leftover $server references

v2.3 October 14, 1995

  • Added allow_html option
  • Added URL field for visitors
  • Security fix: Fixed SSI vulnerability

v2.2 August 1995

  • Added entry_order option (newest/oldest first)
  • Added remote_mail auto-reply feature
  • Bug fixes for mail and display

v2.1 May-June 1995

  • Added email notification
  • Added log option
  • Added linkmail option
  • Added separator and redirection options

v1.1 - v2.0 April-May 1995

  • Initial release with basic guestbook functionality
  • Added newest-first ordering
  • Added auto-redirect after signing