PerlScriptingWeb automationSince 1987

The language that wired the web—and still gets things done.

Explore practical Perl, compare it honestly with modern alternatives, trace the programmable web from CGI to today, and use tools built for real automation work.

One request, four eras

move along the trace

full trace: 1987 → open →

1987 · Perl 1.0

no request to show Pre-web
Built for
report generation, text extraction, system glue
Runs on
any Unix that already had a shell and a C compiler
The web
does not exist yet

Nothing to show here, and that is the point. There is no HTTP request in 1987 — the proposal that becomes the web is still two years away. Everything in the three nodes that follow is downstream of a language designed for text and glue, which is exactly why it was already installed on the machines the web arrived on.

1997 · CGI.pm enters core

hello.cgi Fork per request
#!/usr/bin/perl
use CGI;
my $q    = CGI->new;
my $name = $q->param('name');

print $q->header('text/html');
print "<h1>Hello, $name</h1>";← unescaped

The web is writable. The server forks, runs the interpreter, reads the reply from standard output, and the process exits. Line 7 puts the parameter straight into markup unescaped — the shape behind a very large share of that decade's vulnerabilities.

c. 2009 · PSGI and Plack

app.psgi Interface, not framework
use Plack::Request;

my $app = sub {
    my $req  = Plack::Request->new(shift);
    my $name = $req->parameters->{name} // 'world';

    return [ 200, ['Content-Type' => 'text/plain'], ["Hello, $name\n"] ];← the whole spec
};

A hashref in, an arrayref out. That return value on line 7 — status, headers, body — is the whole specification. The application stops caring which server runs it, which is the decoupling the CGI.pm era never made.

Now · Mojolicious::Lite on PSGI

app.pl Resident interpreter
use Mojolicious::Lite -signatures;

get '/hello' => sub ($c) {
    my $name = $c->param('name') // 'world';
    $c->render(json => { hello => $name });← encoded, not concatenated
};

app->start;

Same request, same parameter, same eight lines of intent. What changed is not syntax: the interpreter stays resident between requests, the response is encoded rather than concatenated, and this file runs unmodified under any PSGI server.

Then / Now

The patterns changed less than the machinery underneath them.

Three pairs, each one a historical pattern next to the current one. Every panel below is in the document whether or not scripting runs — the tabs only decide which one is on top.

Fork per request c. 1993–2005
httpd.confThen
ScriptAlias /cgi-bin/ /var/www/cgi-bin/
# every hit: fork, exec, compile, run, exit

Startup cost paid on every single request, including the compile. Concurrency was bounded by process count, not by the work.

Resident application Now
shellNow
plackup -s Starman --workers 4 app.psgi
# compile once, serve many

The interface (PSGI) is separate from the server that speaks it. Swapping Starman for anything else is a command-line change, not a rewrite.

Request path, then and now Two rows. The 1997 row: browser, then httpd, then a forked perl process, then a response assembled on standard output. The current row has the same four stages, but the middle stage is a PSGI server passing an environment hashref to an application that is already loaded, which returns a three-element arrayref. C. 1997 — ONE PROCESS PER REQUEST Browser httpd perl (new process) response GET fork/exec stdout exits after one reply NOW — ONE INTERPRETER, MANY REQUESTS Browser PSGI server app (resident) response GET $env arrayref stays loaded for the next one
Read across, not down. Both rows have four stages and the same contract: a request goes in, a status, headers and a body come back. The change is where the interpreter lives. CGI paid process creation and compilation on every hit; PSGI pays it once at boot. Everything people attribute to “Perl being slow on the web” in 1999 is in that one difference.

Current state · checked by an editor, sourced or marked pending

How verification works
  • Current stable Perl

    Perl 5.44.0

    Verified 12 Aug 2026 perl.org releases

  • Latest ecosystem / security item

    Awaiting editor verification

    Unverified CPAN Security Group

  • Most recently tested example

    No CI run recorded yet

    Unverified public source repository

  • Latest site update

    Astro production build — canonical routes, Timeline, Labs and public source

    Verified Changelog

Three of these four are typed slots with no value in them. That is deliberate: this build ships the shape of the claim and the source an editor must check, not a number we made up to fill the row.

Choose a language

What are you building? The answer is not always Perl.

Pick a shape of work. You get a decision framing with an explicit confidence level and a note on when it is the wrong call — not a winner.

Featured lab · prototype

Legacy URL Mapper

Paste a list of old URLs. It normalises them, groups query-string patterns, flags duplicates and many-to-one collapses, and proposes a review status for each group. You export the CSV and make the decisions yourself.

Data residency Everything runs in your browser. Your URLs are never uploaded, logged or stored — there is no request to any server after the page loads, and nothing is written to local storage.

Worked example · 5 URLs in, 3 groups out

Static preview
A worked example of Legacy URL Mapper output: three groups classified as unambiguous, duplicate and manual review.
GroupStatusNormalisedWhy
G001 Unambiguous /main/scripts.html?script=SimpleRing Single URL, single value set. The target still has to exist.
G002 Duplicate /main/scripts.html?script=TotalAVS Pro Two inputs normalise identically — %20 and +. Keep one canonical form.
G003 Manual review /main/scripts2.html?script=… Two distinct values share a path and signature. Collapsing them would be many-to-one.

“Unambiguous” is not “correct”. The tool reports what your list implies about itself. It never fetches a status code and never claims a redirect is right.

Reading

Current practice, honest comparisons, and the history that explains both.

Five published pieces across the sections. Drafts are listed in the section hubs with their status showing — nothing here pretends to be finished when it is not.

NowGuide11 minExamples not CI-tested

Making HTTP requests in Perl in 2026

HTTP::Tiny is in core and does more than people expect. Mojo::UserAgent earns its dependency when you need concurrency or a DOM. Here is where the line falls.

Timeline8 min

The year CGI.pm left core

Removing a module from the standard distribution is a maintenance decision, not a verdict on the code.

Reviewed

Timeline · 1987 → open

The trace does not stop at the nineties.

Seventeen events across five tracks, each with what it changed and what survives. Fifteen carry a verification state you can see. The last node is deliberately open.

  1. LanguageVerified

    Perl 1.0 is posted to Usenet

    One language for the work that previously needed awk, sed, sh and a C program stitched together.

  2. WebUnverified

    CGI takes shape around NCSA httpd

    The web becomes writable by anything that can read environment variables and print to stdout.

  3. EcosystemVerified

    CPAN goes live

    The template most later package ecosystems copied, mirror network included.

  4. WebDate unverified

    PSGI and Plack

    A hashref in, an arrayref out — and Perl web applications stop depending on the server that runs them.

  5. NowPerlCodersOpen node

    PerlCoders relaunches as an independent publication

    Everything after this is unwritten. Corrections and submissions are how it gets written.

Archive

Historical references, so old links land somewhere honest.

These are not products. Nothing here is available, supported or recommended, and no binary or source is redistributed. They exist because archived indexes still point at their URLs.

DiscontinuedCGI script

SimpleRing

A dated 2003 capture documents a webring administration script with site-owner controls and manual or automatic acceptance.

  • /main/scripts.html?script=SimpleRing

DiscontinuedCGI script

BannerFarm

Click-counting scripts of this era commonly redirected to an unvalidated target parameter. No advisory located; treat as unsafe.

  • /main/scripts.html?script=BannerFarm

DiscontinuedDocumented

SimleGallery

A scheduled gallery rotator with thumbnail generation, documented by a dated capture and a surviving topical forum link.

  • /main/scripts2.html?script=SimleGallery

Pulse

There is no community activity to show you yet, so we are not going to fake any.

PerlCoders relaunched in 2026. Rather than render empty forum categories or a zero next to a heart icon, here is what actually helps right now.

  1. 01 Ask a question Answered in a Pulse issue or as a guide, with attribution if you want it. Nothing is published without editorial review.
  2. 02 Submit a correction Especially on anything the Timeline marks unverified. A primary source for one of those is the single most useful thing you can send us.
  3. 03 Submit an artifact Old scripts, screenshots, mailing-list captures — with rights information. Without rights metadata we record the reference but do not display the image.