Allow visitors to sign your guestbook and leave comments on your website. One of the most popular CGI scripts from the early web era.
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."
Today's comment systems offer spam protection, moderation, and social features the original guestbook couldn't provide:
Uses GitHub Discussions. Supports replies, reactions, moderation. No rate limits, self-hosted option.
Free GitHub DiscussionsUses GitHub Issues. Lightweight, privacy-focused, easy setup. Great for tech blogs.
Free GitHub Issues| Feature | 1995 Guestbook | Modern Systems |
|---|---|---|
| Spam protection | None | AI/CAPTCHA |
| Moderation | Manual edit files | Admin panel |
| Replies/threading | No | Yes |
| Social login | No | Google, GitHub, etc. |
| Notifications | Email only | Email, Slack, Telegram |
| Reactions/voting | No | Yes |
| Mobile-friendly | No | Responsive |
| Server required | Yes | Optional |
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:
| 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) |
<br> tags<hr> or <p>Download the guestbook package and extract it to your local machine.
Upload the files to your server:
guestbook.pl → your CGI-bin directoryguestbook.html → your web directoryaddguest.html → your web directoryguestlog.html → your web directory (if using logging)# Make the script executable
chmod 755 guestbook.pl
# Make HTML files writable by the server
chmod 666 guestbook.html
chmod 666 guestlog.html
Edit guestbook.pl and set the required variables (see Configuration section below).
Edit addguest.html and update the form action to point to your guestbook.pl location:
<form method="POST" action="/cgi-bin/guestbook.pl">
| 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" |
| 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> |
| Variable | Description | Example |
|---|---|---|
$recipient |
Your email address | "[email protected]" |
$mailprog |
Path to sendmail | "/usr/sbin/sendmail" |
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);
<!-- 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 the Guestbook script package in your preferred format:
See the Guestbook script in action:
More examples can be found in the "In Action" section below.
This is usually a permissions issue. Make sure:
guestbook.html file has write permissions: chmod 666 guestbook.html$guestbookreal variable points to the correct system pathMail issues are common. Check the following:
$mailprog path is correct (usually /usr/sbin/sendmail or /usr/lib/sendmail)$recipient contains a valid email addressThe date is generated by Perl's localtime() function. If it's not appearing:
While the original third-party guestbook hosting services are mostly gone, modern alternatives include:
Spam prevention wasn't built into the original script. Modern approaches include:
The original 1995 script has known security vulnerabilities, including:
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:
$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:
See examples of Guestbook implementations around the web:
These are historical examples showing how the script was used on real websites.
Discussion board and message forum - great for more interactive community features.
Email form handler - if you just need to receive form submissions via email.
Visitor counter - track how many people visit your guestbook.
Visitor logging script - track visitors without a visible guestbook.