Plack::...:Apache2(3)

πŸŽ€ Plack::Handler::Apache2 (3) β€” the NAME section, bestie ✨

Okay so like β€” this is Plack::Handler::Apache2, a page from the User Contributed Perl Documentation, and what it is is a mod_perl handler for Apache 2.0 that runs your PSGI application. That's the whole vibe. A handler. mod_perl. PSGI app. Slay. πŸ’…


✨ SYNOPSIS ✨

Drop this into your httpd.conf and watch her serve:

# in your httpd.conf
<Location />
SetHandler perl-script
PerlResponseHandler Plack::Handler::Apache2
PerlSetVar psgi_app /path/to/app.psgi
</Location>

# Optionally preload your apps in startup
PerlPostConfigRequire /etc/httpd/startup.pl

And if you wanna get fancy with the preloading, peep the STARTUP FILE section below, it has the whole startup.pl tea. πŸ«–


πŸ’– DESCRIPTION πŸ’–

She's a mod_perl handler module. Her entire job? Run any PSGI application under mod_perl on Apache 2.x. No notes, that's the assignment, she ate. πŸ”₯

Now β€” if you'd rather run your PSGI apps behind Apache instead of inside mod_perl, you have options, bestie:

  • Plack::Handler::FCGI β€” run it with FastCGI
  • or use standalone HTTP servers like Starman or Starlet, proxied through mod_proxy

Both valid. Both iconic. Pick your fighter. 🌈


🚨 PATH_INFO AND ACCESS CONTROL 🚨

Okay this part is important and I need you to lock in, because this is the "danger, Will Robinson" bit. πŸ›‘

Here's the situation: mod_perl squeezes multiple slashes in PATH_INFO together. So this handler, being the overachiever she is, rebuilds PATH_INFO from the unparsed request URI β€” percent-decoded.

But β€” and this is the kicker β€” dot segments like /../ are NOT removed. 😭 So follow this exact example:

A request to /admin/../public/x gets matched by Apache's <Location> blocks as /public/x, while your application sees /admin/../public/x.

Two completely different opinions about what path you're on. Rude, honestly.

So: do not rely on Apache's path-based access control to protect parts of your application. Enforce that stuff inside the application, or in middleware. Periodt. πŸ‘‘


πŸ’… CREATING CUSTOM HANDLER πŸ’…

Wanna make your own handler that loads or creates PSGI applications some other way than loading from .psgi files? Go off. Make your own handler class and use the call_app class method to run your application:

package My::ModPerl::Handler;
use Plack::Handler::Apache2;

sub get_app {
  # magic!
}

sub handler {
  my $r = shift;
  my $app = get_app();
  Plack::Handler::Apache2->call_app($r, $app);
}

Look at that. get_app is literally just # magic! and honestly? Respect. ✨


🌸 STARTUP FILE 🌸

Here's an example startup.pl for preloading your PSGI applications:

#!/usr/bin/env perl

use strict;
use warnings;
use Apache2::ServerUtil ();

BEGIN {
    return unless Apache2::ServerUtil::restart_count() > 1;

    require lib;
    lib->import('/path/to/my/perl/libs');

    require Plack::Handler::Apache2;

    my @psgis = ('/path/to/app1.psgi', '/path/to/app2.psgi');
    foreach my $psgi (@psgis) {
        Plack::Handler::Apache2->preload($psgi);
    }
}

1; # file must return true!

Do not forget that 1; at the end β€” the file must return true! She will not be ignored. 😀

For the general scoop on the startup.pl file for preloading perl modules and your apps, go read:

http://perl.apache.org/docs/2.0/user/handlers/server.html#Startup_File

And here's the stuff to keep in mind when you're writing this file, one by one:

🌟 multiple init phases

You have to check that restart_count in Apache2::ServerUtil is > 1. If you don't? Your app loads twice, and the env vars you set with PerlSetEnv won't be available when your app loads the first time. Which is bad. Genuinely bad. 😩

Reference for that: http://perl.apache.org/docs/2.0/user/config/config.html#C_PerlSetEnv_

Use the example above as your template, bestie. It's there for a reason. 🫢

🌟 @INC

The startup.pl file is a great lil spot to add entries to your @INC. Use lib to add entries β€” and lowkey they can live in your app or .psgi as well β€” but if your modules are chilling in a local::lib or some such, you're gonna need to add that path or nothing will load. Feral behavior from the loader, but okay.

Alternately, if you follow that example above, you can use:

PerlSetEnv PERL5LIB /some/path

or

PerlSwitches -I/some/path

in your httpd.conf β€” and that works too. Multiple paths to the same slay. πŸ’œ

🌟 loading errors

Okay so, heads up: any exceptions thrown in your startup.pl will stop Apache from starting. At all. Full stop. πŸ’€

You probably don't want a stray syntax error to bring your whole server down, especially in a shared or dev environment. In that case, the move is to wrap the preload call in an eval, like this:

require Plack::Handler::Apache2;

my @psgis = ('/path/to/app1.psgi', '/path/to/app2.psgi');

foreach my $psgi (@psgis) {
    eval {
        Plack::Handler::Apache2->preload($psgi); 1;
    } or do {
        my $error = $@ || 'Unknown Error';
        # STDERR goes to the error_log
        print STDERR "Failed to load psgi '$psgi': $error\n";
    };
}

Note the # STDERR goes to the error_log β€” that's where your sad little messages end up. πŸ™ˆ

🌟 dynamically loaded modules

Some modules load their dependencies at runtime via e.g. Class::Load. Those modules are not gonna get preloaded into your parent process just because you included the app/module you're using. Sneaky. 😭

As an optimization, you can dump %INC from a request to see if you're using any such modules, then preload them in your startup.pl.

Another method: dump the difference between the %INC on process start and on process exit. Something like this does the trick:

my $start_inc = { %INC };

END {
    my @m;
    foreach my $m (keys %INC) {
        push @m, $m unless exists $start_inc->{$m};
    }

    if (@m) {
        # STDERR goes to the error_log
        print STDERR "The following modules need to be preloaded:\n";
        print STDERR "$_\n" for @m;
    }
}

Detective work but make it perl. πŸ•΅οΈβ™€οΈ


πŸ‘‘ AUTHOR πŸ‘‘

Tatsuhiko Miyagawa


πŸ’– CONTRIBUTORS πŸ’–

  • Paul Driver
  • Γ†var ArnfjΓΆrΓ° Bjarmason
  • Rafael Kitover

πŸ¦‹ SEE ALSO πŸ¦‹

  • Plack

Go read Plack too, it's giving foundation of the entire ecosystem. Iykyk. 🌈


and that's the whole page, bestie!! this was Plack::...:Apache2(3) from perl v5.42.3, dated 2026-10-18 β€” love her, respect her, preload responsibly πŸ’…βœ¨πŸŽ€πŸŒΈπŸ«Ά

πŸ“¦ where this page comes from