Plack::...r::FCGI(3)

πŸ’– NAME πŸ’–

Plack::Handler::FCGI β€” the FastCGI handler for Plack, bestie πŸ¦‹


✨ SYNOPSIS ✨

okay so there are three whole ways to live your life here, pick your fighter:

# Run as a standalone daemon
plackup -s FCGI --listen /tmp/fcgi.sock --daemonize --nproc 10

# Run from your web server like mod_fastcgi
#!/usr/bin/env plackup -s FCGI
my $app = sub { ... };

# Roll your own
my $server = Plack::Handler::FCGI->new(
    nproc  => $num_proc,
    listen => [ $port_or_socket ],
    detach => 1,
);
$server->run($app);

🌸 DESCRIPTION 🌸

So this is a handler module, right, and its whole job is running any PSGI application β€” either as a standalone FastCGI daemon, or as a .fcgi script. That's it. That's the gig. And it ate. πŸ’…


πŸŽ€ OPTIONS πŸŽ€

okay bestie, options menu time, no skips, we're reading the whole thing:

  • listen β€” listen on a socket path, a hostname:port, or just :port. The vibes:
listen => [ '/path/to/socket' ]
listen => [ ':8080' ]
  • port β€” listen via TCP on port, on all interfaces. Same exact thing as listen => ":$port". πŸ‘
  • leave-umask β€” set it to 1 if you do NOT want the umask set to 0 when the socket opens.
  • nointr β€” don't let the listener get interrupted by Ctrl+C. Unbothered. Uninterrupted. πŸ’€
  • nproc β€” specify a number of processes for FCGI::ProcManager.
  • pid β€” specify a filename for the pid file.
  • manager β€” specify either a FCGI::ProcManager subclass, or an actual FCGI::ProcManager-compatible object. And listen: if you do not want a FCGI::ProcManager and instead wanna run in a single process, set this to undef. Like this:
use FCGI::ProcManager::Dynamic;
Plack::Handler::FCGI->new(
    manager => FCGI::ProcManager::Dynamic->new(...),
);
  • daemonize β€” daemonize the process. main character energy, backgrounded 🫢
  • proc-title β€” specify process title.
  • keep-stderr β€” send psgi.errors to STDERR instead of to the FCGI error stream.
  • backlog β€” maximum length of the queue of pending connections. Defaults to 100.

🧚 EXTENSIONS 🧚

Supported PSGI::Extensions. yes, plural, we're fancy πŸ’œ

  • psgix.cleanup β€” supports the psgix.cleanup extension! To use it, you just push a callback onto $env->{'psgix.cleanup.handlers'. These callbacks all run after the pm_post_dispatch hook, fr fr.
push @{ $env->{'psgix.cleanup.handlers'} }, sub { warn "Did this later" }
    if $env->{'psgix.cleanup'};
  • psgix.harakiri β€” if there's a manager, then psgix.harakiri gets enabled, and setting $env->{'psgix.harakiri.commit'} to a true value causes $manager->pm_exit to be called after the request is finished. dramatic exit, iconic πŸŽ€
$env->{'psgix.harakiri.commit'} = 1
    if $env->{'psgix.harakiri'};

🌈 WEB SERVER CONFIGURATIONS 🌈

In all of these cases you're gonna want to install FCGI and FCGI::ProcManager. Biggest hack: just install Task::Plack, which literally includes both of them. obsessed. 😌

⭐ nginx

Here's an example nginx config to run your FCGI daemon on a Unix domain socket and serve it at the server's root URL (/):

http {
  server {
    listen 3001;
    location / {
      set $script "";
      set $path_info $uri;
      fastcgi_pass unix:/tmp/fastcgi.sock;
      fastcgi_param  SCRIPT_NAME      $script;
      fastcgi_param  PATH_INFO        $path_info;
      fastcgi_param  QUERY_STRING     $query_string;
      fastcgi_param  REQUEST_METHOD   $request_method;
      fastcgi_param  CONTENT_TYPE     $content_type;
      fastcgi_param  CONTENT_LENGTH   $content_length;
      fastcgi_param  REQUEST_URI      $request_uri;
      fastcgi_param  SERVER_PROTOCOL  $server_protocol;
      fastcgi_param  REMOTE_ADDR      $remote_addr;
      fastcgi_param  REMOTE_PORT      $remote_port;
      fastcgi_param  SERVER_ADDR      $server_addr;
      fastcgi_param  SERVER_PORT      $server_port;
      fastcgi_param  SERVER_NAME      $server_name;
    }
  }
}

If you wanna host your app in a non-root path, you should mangle this config so the path goes into SCRIPT_NAME and the rest of the path goes into PATH_INFO.

More details right here: https://www.nginx.com/resources/wiki/start/topics/examples/fastcgiexample/


πŸ’… PATH_INFO AND ACCESS CONTROL πŸ’…

okay this part is important, pay attention bestie:

Some web servers squeeze multiple slashes in PATH_INFO, so this handler just ignores the PATH_INFO passed by the web server and rebuilds it from REQUEST_URI β€” percent-decoded, with SCRIPT_NAME stripped. Dot segments like /../ are not removed. 🚨

The web server, on the other hand, usually matches its location or <Location> blocks against the normalized path. So a request to /admin/../public/x gets matched by the web server as /public/x, but your application sees PATH_INFO as /admin/../public/x. Do not rely on path-based access control in the front web server to protect parts of your application. Enforce it in the application (or in middleware) instead, or make absolutely sure the web server rejects request URIs containing dot segments. periodt.

no cap, this is a real security-shaped thing, do not skip it πŸ™ˆ

πŸ”₯ Apache mod_fastcgi

After installing mod_fastcgi, add the FastCgiExternalServer directive to your Apache config:

FastCgiExternalServer /tmp/myapp.fcgi -socket /tmp/fcgi.sock

## Then set up the location that you want to be handled by fastcgi:

# EITHER from a given path
Alias /myapp/ /tmp/myapp.fcgi/

# OR at the root
Alias / /tmp/myapp.fcgi/

Now you can use plackup to listen to the socket you just configured in Apache:

$  plackup -s FCGI --listen /tmp/myapp.sock psgi/myapp.psgi

The above describes the "standalone" method, which is usually the appropriate one. There are other methods, described in more detail at "Standalone_server_mode" in Catalyst::Engine::FastCGI (that one's about Catalyst, but you can set it up similarly for Plack).

Also see http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html#FastCgiExternalServer for more details.

πŸ¦‹ lighttpd

To host the app in the root path, you're recommended to use lighttpd 1.4.23 or newer with the fix-root-scriptname flag, like this:

fastcgi.server = ( "/" =>
   ((
     "socket" => "/tmp/fcgi.sock",
     "check-local" => "disable",
     "fix-root-scriptname" => "enable",
   ))

If you're on lighttpd older than 1.4.22, where you don't have fix-root-scriptname, mounting apps under the root causes the wrong SCRIPT_NAME and PATH_INFO to be set. ALSO, mounting under the empty root ("") or a path with a trailing slash would still set weird values even with fix-root-scriptname. In those cases you can use Plack::Middleware::LighttpdScriptNameFix to fix it. 🩹

To mount in a non-root path over TCP:

fastcgi.server = ( "/foo" =>
   ((
     "host" = "127.0.0.1",
     "port" = "5000",
     "check-local" => "disable",
   ))

It's recommended that your mount path does NOT have a trailing slash. If you really really need one, consider using Plack::Middleware::LighttpdScriptNameFix to fix the wrong PATH_INFO values lighttpd sets.


πŸ‘‘ Authorization πŸ‘‘

Most fastcgi configuration does not pass Authorization headers into the HTTP_AUTHORIZATION environment variable by default, and that's for security reasons. But auth middleware like Plack::Middleware::Auth::Basic or Catalyst::Authentication::Credential::HTTP needs that variable set up. Plack::Handler::FCGI supports extracting the Authorization environment variable when it's configured that way. slay 🫢

Apache2 with mod_fastcgi:

--pass-header Authorization

mod_fcgid:

FcgidPassHeader Authorization

🌟 Server::Starter 🌟

This plack handler supports Server::Starter as a superdaemon. Simply launch plackup from start_server with a path option. Heads up: the listen option is ignored when launched from Server::Starter.

start_server --path=/tmp/socket -- plackup -s FCGI app.psgi

πŸ’– SEE ALSO πŸ’–

Plack


aaaand that's the whole page, bestie πŸŽ€ this was Plack::...r::FCGI(3) from the User Contributed Perl Documentation, perl v5.42.3, dated 2026-10-18. go forth and daemonize responsibly βœ¨πŸ¦‹πŸ’œ

πŸ“¦ where this page comes from