HTTP Cookie Library

A Perl 4/5 compatible library for easily managing Persistent Client State HTTP Cookies in CGI scripts.

Perl v2.1 Library 1990s Classic

Quick Info

  • Version: 2.1
  • Released: December 23, 1996
  • Language: Perl 4/5
  • Type: Library (not standalone)
  • License: Free for use

Overview

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.

Key Features
  • Get cookies from the browser environment
  • Set single or multiple cookies at once
  • Compress multiple cookies into one (bypass 20 cookie limit)
  • Set expiration dates, domains, and paths
  • Secure cookie support for HTTPS
  • Automatic URL-encoding for special characters
Important Notes
  • This is a library file, not a standalone script
  • Requires Perl 4 or Perl 5
  • Include with require 'cookie.lib';
  • Must output cookies before any HTML content
  • Cookie data stored in %Cookies hash

How HTTP Cookies Work

The Cookie Exchange Process

Browser
CGI Script
Cookie Storage
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

Cookie Anatomy

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)
Did You Know?

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.

1990s Limitations
  • Max 20 cookies per domain
  • 4KB size limit per cookie
  • 300 total cookies in browser
  • No HttpOnly attribute until 2002
Cookie History
  • 1994: Invented at Netscape
  • 1995: First browser support
  • 1997: RFC 2109 specification
  • 2000: RFC 2965 (Set-Cookie2)
  • 2011: RFC 6265 (current spec)

API Reference

Available Subroutines

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!
Warning: Always call &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
Compressed cookies use a delimiter to pack multiple key=value pairs into one cookie string, then unpack them when reading.

Code Examples

Complete Login Example (Perl CGI)
#!/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 Equivalent (Modern)
<?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
}
?>
Client-Side JavaScript (with js-cookie library)
// 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=/";
Node.js with Express
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);

Historical Context

In 1996, when this library was released, cookies were a relatively new technology. Web developers needed tools to:

Track Users

Before databases, cookies were the primary way to remember users between visits.

Shopping Carts

E-commerce sites stored entire shopping carts in cookies.

Preferences

User preferences like language and theme were stored client-side.

Modern Alternatives (2024)

Modern languages have built-in cookie support. For JavaScript, dedicated libraries provide a cleaner API.

Built-in Language Support

PHP

Native setcookie() and $_COOKIE superglobal. PHP 7.3+ supports SameSite attribute.

setcookie('name', 'value', [
    'expires' => time() + 3600,
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax'
]);
Python (Flask/Django)

Flask's response.set_cookie() and Django's HttpResponse.set_cookie().

# Flask
response.set_cookie(
    'name', 'value',
    max_age=3600,
    secure=True,
    httponly=True
)
Ruby on Rails

Rails provides cookies hash with encryption and signing support.

# Rails
cookies.encrypted[:user_id] = {
    value: user.id,
    expires: 1.week.from_now,
    httponly: true
}

JavaScript Cookie Libraries

js-cookie
Recommended

Lightweight, simple API, no dependencies. The most popular JavaScript cookie library.

  • 2KB minified
  • JSON support
  • All cookie attributes
  • TypeScript definitions
universal-cookie
React

Universal cookie library for React applications. Works with SSR (Next.js).

  • React hooks support
  • Server-side rendering
  • TypeScript support
cookie-parser
Node.js

Express middleware for parsing cookies. Industry standard for Node.js backends.

  • Express integration
  • Signed cookies
  • JSON cookies

Comparison: 1996 vs 2024

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

Download

HTTP Cookie Library package includes:

  • cookie.lib Main library file
  • ccounter.pl Example script
  • README Documentation
Quick Install
# Extract and install
tar -xzf cookielib.tar.gz
cp cookie.lib /path/to/cgi-bin/

# In your Perl script:
require 'cookie.lib';

Frequently Asked Questions

HTTP cookies are small pieces of data that a web server sends to a user's browser, which stores them and sends them back with subsequent requests. They are used for session management (login state), personalization (user preferences), and tracking (analytics). Without cookies, every page request would be independent with no memory of previous interactions.

The most common reason is outputting content before setting cookies. Cookies must be sent as HTTP headers, which must come BEFORE any HTML content. Make sure you call &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.

To delete a cookie, set it again with an expiration date in the past. Use &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.

In the 1990s, browsers limited each domain to 20 cookies maximum. This library's &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.

Cookies themselves are not encrypted and can be read by JavaScript (unless HttpOnly) or intercepted over HTTP (unless Secure). Modern best practices: (1) Use 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.

Use cookies when: data needs to be sent to the server automatically, you need server-side session management, or data must work across subdomains. Use localStorage when: data is only needed client-side, you need more than 4KB storage, or data doesn't need to be sent with every request. Use sessionStorage for data that should clear when the browser closes.

Related Scripts

Back to Scripts