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.
PerlScriptingWeb automationSince 1987
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.
1987 · Perl 1.0
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
#!/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
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
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
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.
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.
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.
1. Upload script.pl in ASCII mode
2. chmod 755 script.pl
3. Edit $MAILPROG at the top
# upgrades: none. patching: manual.
Distribution was a download and a text editor. There was no way to learn that the copy you were running had a published flaw.
requires 'Mojolicious', '>= 9.0';
requires 'JSON::PP';
# cpanm --installdeps . ; audit against advisories
A declared dependency set is what makes an advisory actionable. The CPAN Security Group exists because the manifest exists.
print "<p>$comment</p>\n";
# correct only when $comment happens to be safe
Output escaping was a thing you did by hand, in every branch, forever. Missing one was the default outcome, not the exception.
<p><%= $comment %></p>
# <%= %> escapes; <%== %> is the deliberate opt-out
The safe path became the short one. That inversion — not any single framework — is the real security difference between the two eras.
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
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
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.
| Group | Status | Normalised | Why |
|---|---|---|---|
| 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
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.
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.
One task, two implementations, and a decision matrix that names its own limits.
Reviewed
Four vulnerability classes in ninety lines, then a rewrite that keeps the behaviour and drops the execution model.
Reviewed
Removing a module from the standard distribution is a maintenance decision, not a verdict on the code.
Reviewed
Method, sample, and the part where we say which of our numbers you should not rely on.
Reviewed
Timeline · 1987 → open
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.
LanguageVerified
One language for the work that previously needed awk, sed, sh and a C program stitched together.
WebUnverified
The web becomes writable by anything that can read environment variables and print to stdout.
EcosystemVerified
The template most later package ecosystems copied, mirror network included.
WebDate unverified
A hashref in, an arrayref out — and Perl web applications stop depending on the server that runs them.
NowPerlCodersOpen node
Everything after this is unwritten. Corrections and submissions are how it gets written.
Archive
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.
A dated 2003 capture documents a webring administration script with site-owner controls and manual or automatic acceptance.
Click-counting scripts of this era commonly redirected to an unvalidated target parameter. No advisory located; treat as unsafe.
A scheduled gallery rotator with thumbnail generation, documented by a dated capture and a surviving topical forum link.
Pulse
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.