Allow visitors to add their own links to your website, creating a community-driven link directory.
Free For All Links (FFA Links) creates a page where anyone can add their website URL and description. The script manages the link list, preventing duplicates and organizing submissions automatically.
"Free For All" means anyone can submit a link without registration or approval. This democratized web discovery before search engines became dominant.
In the pre-Google era, webmasters needed ways to get discovered. FFA pages provided free backlinks and exposure to new audiences.
At their peak, there were over 500,000 FFA pages on the web. Some webmasters used automated tools to submit their links to thousands of FFA pages at once!
"The best way to promote your website is through link exchanges and FFA pages. Submit to as many as possible!"
Visitors submit links through a simple HTML form with fields for URL, title, and description.
Automatically checks for duplicate URLs to prevent the same link from being added twice.
Set maximum number of links. Oldest links are removed when limit is reached (FIFO).
Optional email alerts when new links are submitted to your FFA page.
Links are automatically sorted by date, keeping newest submissions at the top.
HTML templates for the submission form and link display pages.
The FFA Links script follows a simple request-response pattern typical of 1990s CGI applications:
| Step | Action | Details |
|---|---|---|
| 1 | Visitor fills form | Enters URL, title, description, and optionally email address |
| 2 | Form submitted | Data sent via POST to /cgi-bin/links.pl |
| 3 | Validation | Script checks for required fields and duplicate URLs |
| 4 | Storage | Link appended to flat file database (links.txt) |
| 5 | Notification | Email sent to admin (if configured) |
| 6 | Confirmation | Thank you page displayed to visitor |
<!-- Link Submission Form -->
<form action="/cgi-bin/links.pl" method="POST">
<table border="0" cellpadding="5">
<tr>
<td><b>URL:</b></td>
<td><input type="text" name="url" size="50"
value="http://"></td>
</tr>
<tr>
<td><b>Title:</b></td>
<td><input type="text" name="title" size="50"></td>
</tr>
<tr>
<td valign="top"><b>Description:</b></td>
<td><textarea name="description" rows="3"
cols="50"></textarea></td>
</tr>
<tr>
<td><b>Your Email:</b></td>
<td><input type="text" name="email" size="50"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Add Link"></td>
</tr>
</table>
</form>
#!/usr/bin/perl
# links.pl - Free For All Links Script
# Classic CGI Script Archive
# Configuration
$links_file = "links.txt";
$max_links = 100;
$admin_email = "[email protected]";
# Parse form data
&parse_form;
# Validate input
if ($FORM{'url'} eq '' || $FORM{'title'} eq '') {
&error("URL and Title are required!");
}
# Check for duplicates
open(LINKS, $links_file);
@links = <LINKS>;
close(LINKS);
foreach $line (@links) {
if ($line =~ /$FORM{'url'}/i) {
&error("This URL already exists!");
}
}
# Add new link
$new_link = join("|",
$FORM{'url'},
$FORM{'title'},
$FORM{'description'},
time()
);
# Prepend to file (newest first)
unshift(@links, "$new_link\n");
# Trim to max links
if (@links > $max_links) {
@links = @links[0..$max_links-1];
}
# Save
open(LINKS, ">$links_file");
print LINKS @links;
close(LINKS);
# Send notification
&send_mail($admin_email, "New Link Added",
"URL: $FORM{'url'}\nTitle: $FORM{'title'}");
# Show confirmation
print "Content-type: text/html\n\n";
print "<h1>Link Added!</h1>";
print "<p>Thank you for your submission.</p>";
<?php
// links.php - PHP version of FFA Links
$links_file = "links.txt";
$max_links = 100;
$admin_email = "[email protected]";
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$url = trim($_POST['url']);
$title = htmlspecialchars($_POST['title']);
$description = htmlspecialchars($_POST['description']);
// Validate
if (empty($url) || empty($title)) {
die("URL and Title are required!");
}
// Validate URL format
if (!filter_var($url, FILTER_VALIDATE_URL)) {
die("Invalid URL format!");
}
// Read existing links
$links = file_exists($links_file)
? file($links_file, FILE_IGNORE_NEW_LINES)
: array();
// Check duplicates
foreach ($links as $link) {
if (stripos($link, $url) !== false) {
die("This URL already exists!");
}
}
// Add new link
$new_link = implode("|", array(
$url, $title, $description, time()
));
array_unshift($links, $new_link);
// Trim to max
$links = array_slice($links, 0, $max_links);
// Save
file_put_contents($links_file, implode("\n", $links));
// Notify admin
mail($admin_email, "New Link", "URL: $url\nTitle: $title");
echo "<h1>Link Added!</h1>";
}
?>
// Modern link directory with Express.js
const express = require('express');
const { body, validationResult } = require('express-validator');
const rateLimit = require('express-rate-limit');
const mongoose = require('mongoose');
const app = express();
// Rate limiting to prevent spam
const submitLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5 // 5 submissions per window
});
// Link Schema with moderation
const linkSchema = new mongoose.Schema({
url: { type: String, required: true, unique: true },
title: { type: String, required: true, maxlength: 100 },
description: { type: String, maxlength: 500 },
submittedBy: String,
status: {
type: String,
enum: ['pending', 'approved', 'rejected'],
default: 'pending'
},
createdAt: { type: Date, default: Date.now }
});
const Link = mongoose.model('Link', linkSchema);
// Submit link with validation
app.post('/api/links',
submitLimiter,
[
body('url').isURL().withMessage('Valid URL required'),
body('title').trim().isLength({ min: 3, max: 100 }),
body('description').optional().trim().isLength({ max: 500 })
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const link = new Link(req.body);
await link.save();
// Links go to moderation queue by default
res.json({
message: 'Link submitted for review',
status: 'pending'
});
} catch (err) {
if (err.code === 11000) {
return res.status(400).json({
error: 'URL already exists'
});
}
res.status(500).json({ error: 'Server error' });
}
}
);
// Get approved links only
app.get('/api/links', async (req, res) => {
const links = await Link.find({ status: 'approved' })
.sort({ createdAt: -1 })
.limit(100);
res.json(links);
});
| Feature | FFA Links (1990s) | Modern Directory Software (2024) |
|---|---|---|
| Storage | Flat text files | SQL/NoSQL databases, full-text search |
| Moderation | None (or manual) | Approval queues, AI spam detection |
| User Accounts | None | Full user management, OAuth |
| Categories | Single list or basic categories | Hierarchical taxonomy, tags, filters |
| Search | None or basic grep | Full-text, faceted search, Elasticsearch |
| Spam Protection | None | CAPTCHA, rate limiting, AI filters |
| SEO | Basic HTML | Schema.org, sitemaps, canonical URLs |
| Monetization | Banner ads | Featured listings, subscriptions, ads |
| API | None | REST/GraphQL APIs |
| Mobile | Not supported | Responsive design, mobile apps |
Today's directory software offers moderation, SEO optimization, monetization, and spam protection that 1990s FFA scripts couldn't provide.
Professional PHP-based directory software with SEO optimization, multiple payment gateways, and extensive customization.
Feature-rich directory script with cross-linking, reciprocal link checker, and built-in blog functionality.
Simple open-source PHP link directory script. Good for learning and small projects.
All-in-one platform for building membership-based directories with recurring revenue features.
Enterprise directory platform with powerful search, maps integration, and mobile apps.
Low-code platform for building custom directory websites with drag-and-drop interface.
Location-based business directory with maps, reviews, and custom fields.
Free + ProFull-featured directory with payment integration and front-end management.
Free + ProSimple link directory specifically designed for affiliate and resource links.
FreeLightweight directory solution built on Jetkoo framework.
Free + ProFree For All Links is available in several archive formats:
links.pl - Main CGI scriptlinks.txt - Data file templatelinks.html - Submission form templateREADME - Installation instructions