A Perl 4/5 compatible library for easily managing Persistent Client State HTTP Cookies in CGI scripts.
HTTP Cookie Library provides easy-to-use subroutines for working with browser cookies in your Perl CGI scripts. It handles all the complexity of cookie encoding, expiration dates, domains, and paths, making cookie management straightforward for web developers.
require 'cookie.lib';%Cookies hash| Step | Direction | Description |
|---|---|---|
| 1 | Server → Browser | Server sends Set-Cookie header with cookie data |
| 2 | Browser | Browser stores cookie locally (memory or disk) |
| 3 | Browser → Server | On subsequent requests, browser sends Cookie header |
| 4 | Server | CGI script reads HTTP_COOKIE environment variable |
Set-Cookie: name=value; expires=Thu, 01 Jan 2025 00:00:00 GMT; path=/; domain=.example.com; secure; HttpOnly
| Attribute | Required | Description |
|---|---|---|
name=value |
Yes | The cookie name and its value |
expires |
No | When the cookie expires (session cookie if omitted) |
path |
No | URL path where cookie is valid (default: current path) |
domain |
No | Domain(s) where cookie is valid |
secure |
No | Only send over HTTPS |
HttpOnly |
No | Not accessible via JavaScript (security) |
The term "cookie" was coined by Lou Montulli at Netscape in 1994, derived from "magic cookie" - a Unix term for a small piece of data passed between programs.
| Subroutine | Parameters | Description |
|---|---|---|
&GetCookies |
('name1', 'name2', ...) |
Retrieve specified cookies from browser. Results stored in %Cookies hash. |
&SetCookies |
('name', 'value', ...) |
Set one or more cookies. Must be called before any output. |
&SetCookieExpDate |
('Mon, DD-Mon-YYYY HH:MM:SS GMT') |
Set expiration date for subsequent cookies. |
&SetCookiePath |
('/path/') |
Set URL path where cookies are valid. |
&SetCookieDomain |
('.example.com') |
Set domain for cookies (dot prefix for subdomains). |
&SetSecureCookie |
() |
Enable secure flag (HTTPS only) for subsequent cookies. |
&SetCompressedCookies |
('name', 'n1', 'v1', ...) |
Compress multiple values into a single cookie (bypass 20 cookie limit). |
&GetCompressedCookies |
('name', 'n1', 'n2', ...) |
Retrieve values from a compressed cookie. |
# Get specific cookies
&GetCookies('username', 'session_id');
# Access values from %Cookies hash
print "Hello, $Cookies{'username'}!\n";
print "Session: $Cookies{'session_id'}\n";
# Check if cookie exists
if (&GetCookies('username')) {
print "Cookie found!\n";
} else {
print "No cookie set.\n";
}
# Set a single cookie
print "Content-type: text/html\n";
&SetCookies('username', 'JohnDoe');
print "\n"; # End headers
# Set multiple cookies at once
print "Content-type: text/html\n";
&SetCookies('user', 'john', 'prefs', 'dark_mode', 'lang', 'en');
print "\n";
# IMPORTANT: Cookies must be set BEFORE the blank line that ends headers!
&SetCookies before printing the blank line that ends HTTP headers.
# Set expiration to specific date
&SetCookieExpDate('Mon, 01-Jan-2025 00:00:00 GMT');
&SetCookies('remember_me', 'yes');
# Delete a cookie (set expiration in the past)
&SetCookieExpDate('Mon, 01-Jan-1990 00:00:00 GMT');
&SetCookies('old_cookie', '');
# Helper: Calculate future date
sub FutureDate {
my ($days) = @_;
my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
my ($sec,$min,$hour,$mday,$mon,$year,$wday) = gmtime(time + $days * 86400);
return sprintf("%s, %02d-%s-%04d %02d:%02d:%02d GMT",
$days[$wday], $mday, $months[$mon], $year+1900, $hour, $min, $sec);
}
# Problem: Browsers only allowed 20 cookies per domain in the 1990s
# Solution: Compress multiple values into a single cookie
# Store multiple preferences in one cookie
&SetCompressedCookies('user_prefs',
'theme', 'dark',
'font_size', '14',
'sidebar', 'collapsed',
'notifications', 'on'
);
# Later, retrieve the compressed values
&GetCompressedCookies('user_prefs', 'theme', 'font_size', 'sidebar');
print "Theme: $Cookies{'theme'}\n"; # dark
print "Font: $Cookies{'font_size'}\n"; # 14
print "Sidebar: $Cookies{'sidebar'}\n"; # collapsed
#!/usr/bin/perl
# login.pl - Simple login with cookies
require 'cookie.lib';
# Check for existing session
if (&GetCookies('session_id', 'username')) {
# User is logged in
print "Content-type: text/html\n\n";
print "<h1>Welcome back, $Cookies{'username'}!</h1>";
print "<a href='logout.pl'>Logout</a>";
}
else {
# Process login form
&ReadParse; # Parse form data
if ($in{'username'} && $in{'password'}) {
# Validate credentials (simplified)
if ($in{'password'} eq 'secret') {
# Set session cookies
my $session = time() . int(rand(10000));
print "Content-type: text/html\n";
# Set expiration to 7 days
&SetCookieExpDate(&FutureDate(7));
&SetCookies('session_id', $session);
&SetCookies('username', $in{'username'});
print "\n";
print "<h1>Login successful!</h1>";
}
else {
print "Content-type: text/html\n\n";
print "<h1>Invalid password!</h1>";
}
}
else {
# Show login form
print "Content-type: text/html\n\n";
print <<HTML;
<form method="POST">
Username: <input name="username"><br>
Password: <input name="password" type="password"><br>
<input type="submit" value="Login">
</form>
HTML
}
}
<?php
// login.php - Modern PHP cookie handling
// Check for existing session
if (isset($_COOKIE['session_id']) && isset($_COOKIE['username'])) {
echo "<h1>Welcome back, " . htmlspecialchars($_COOKIE['username']) . "!</h1>";
echo "<a href='logout.php'>Logout</a>";
}
elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if ($password === 'secret') {
$session = time() . random_int(1000, 9999);
// Set cookies with security options
$options = [
'expires' => time() + (7 * 24 * 60 * 60), // 7 days
'path' => '/',
'domain' => '',
'secure' => true, // HTTPS only
'httponly' => true, // No JavaScript access
'samesite' => 'Lax' // CSRF protection
];
setcookie('session_id', $session, $options);
setcookie('username', $username, $options);
echo "<h1>Login successful!</h1>";
}
else {
echo "<h1>Invalid password!</h1>";
}
}
else {
// Show login form
?>
<form method="POST">
Username: <input name="username"><br>
Password: <input name="password" type="password"><br>
<input type="submit" value="Login">
</form>
<?php
}
?>
// Using js-cookie library (recommended)
// <script src="https://cdn.jsdelivr.net/npm/js-cookie@3/dist/js.cookie.min.js"></script>
// Set a cookie
Cookies.set('username', 'JohnDoe', { expires: 7 }); // 7 days
// Set with all options
Cookies.set('session', 'abc123', {
expires: 7, // Days
path: '/',
domain: '.example.com',
secure: true,
sameSite: 'Lax'
});
// Get a cookie
const username = Cookies.get('username');
console.log('Hello, ' + username);
// Get all cookies
const allCookies = Cookies.get();
// Delete a cookie
Cookies.remove('username');
Cookies.remove('session', { path: '/', domain: '.example.com' });
// ---- Vanilla JavaScript (no library) ----
// Set cookie
document.cookie = "username=JohnDoe; expires=" +
new Date(Date.now() + 7*24*60*60*1000).toUTCString() +
"; path=/; SameSite=Lax";
// Get cookie (helper function)
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
// Delete cookie
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());
app.use(express.urlencoded({ extended: true }));
// Check for session cookie
app.get('/', (req, res) => {
if (req.cookies.session_id && req.cookies.username) {
res.send(`<h1>Welcome back, ${req.cookies.username}!</h1>`);
} else {
res.send(`
<form method="POST" action="/login">
Username: <input name="username"><br>
Password: <input name="password" type="password"><br>
<input type="submit" value="Login">
</form>
`);
}
});
// Handle login
app.post('/login', (req, res) => {
const { username, password } = req.body;
if (password === 'secret') {
const session = Date.now() + Math.random().toString(36);
// Set cookies with security options
const cookieOptions = {
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days in ms
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax'
};
res.cookie('session_id', session, cookieOptions);
res.cookie('username', username, cookieOptions);
res.send('<h1>Login successful!</h1>');
} else {
res.status(401).send('<h1>Invalid password!</h1>');
}
});
// Logout - clear cookies
app.get('/logout', (req, res) => {
res.clearCookie('session_id');
res.clearCookie('username');
res.redirect('/');
});
app.listen(3000);
In 1996, when this library was released, cookies were a relatively new technology. Web developers needed tools to:
Before databases, cookies were the primary way to remember users between visits.
E-commerce sites stored entire shopping carts in cookies.
User preferences like language and theme were stored client-side.
Modern languages have built-in cookie support. For JavaScript, dedicated libraries provide a cleaner API.
Native setcookie() and $_COOKIE superglobal. PHP 7.3+ supports SameSite attribute.
setcookie('name', 'value', [
'expires' => time() + 3600,
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
Flask's response.set_cookie() and Django's HttpResponse.set_cookie().
# Flask
response.set_cookie(
'name', 'value',
max_age=3600,
secure=True,
httponly=True
)
Rails provides cookies hash with encryption and signing support.
# Rails
cookies.encrypted[:user_id] = {
value: user.id,
expires: 1.week.from_now,
httponly: true
}
Lightweight, simple API, no dependencies. The most popular JavaScript cookie library.
Universal cookie library for React applications. Works with SSR (Next.js).
Express middleware for parsing cookies. Industry standard for Node.js backends.
| Feature | cookie.lib (1996) | Modern Solutions (2024) |
|---|---|---|
| Security | No HttpOnly, no Secure by default | HttpOnly, Secure, SameSite attributes |
| Encryption | None | Signed/encrypted cookies available |
| Size Limit | 4KB per cookie, 20 cookies max | Still 4KB, but Web Storage for larger data |
| Privacy | No restrictions | GDPR consent, third-party restrictions |
| Alternatives | Cookies only | localStorage, sessionStorage, IndexedDB |
| Session Mgmt | Manual implementation | JWT, server sessions, OAuth |
HTTP Cookie Library package includes:
cookie.lib
Main library file
ccounter.pl
Example script
README
Documentation
# Extract and install
tar -xzf cookielib.tar.gz
cp cookie.lib /path/to/cgi-bin/
# In your Perl script:
require 'cookie.lib';
&SetCookies before printing the blank line that ends HTTP headers. Also check: (1) the domain matches your site, (2) the path includes your page, (3) Secure cookies require HTTPS, (4) browser hasn't disabled cookies.
&SetCookieExpDate('Mon, 01-Jan-1990 00:00:00 GMT') followed by &SetCookies('cookie_name', ''). The browser will see the expired date and remove the cookie. Make sure to use the same path and domain that were used when setting the cookie.
&SetCompressedCookies function works around this by packing multiple key-value pairs into a single cookie string. Modern browsers have increased limits (typically 50+ cookies per domain), but the 4KB size limit per cookie remains. For large data, consider using server-side sessions or Web Storage APIs.
Secure flag to require HTTPS; (2) Use HttpOnly to prevent XSS attacks; (3) Use SameSite to prevent CSRF; (4) Never store sensitive data directly in cookies; (5) Use server-side sessions with only a session ID in the cookie; (6) Sign or encrypt cookie values for integrity.