Plack::...endfile(3)
β¨ NAME β¨
Plack::Middleware::XSendfile β it sets the X-Sendfile header (or one of its little cousins) so your frontend can do the file-serving for you π
(also hi bestie, this is the page that renders as Plack::...endfile(3) in your terminal, from the User Contributed Perl Documentation collection β the name got squished but she's still iconic π
)
π¦ SYNOPSIS π¦
enable "Plack::Middleware::XSendfile";
π DEPRECATION NOTICE π
Okay so serious talk for a sec. Not me having to tell you this, but this middleware is deprecated and will be removed in a future release. π Why? Because of poor security design β the way config gets passed in via HTTP request headers. Go read the SECURITY section, fr fr.
The simplest glow-up replacement is to just set the appropriate header directly in your application when you're serving a file. For example, in a Mojolicious controller:
sub download {
my $c = shift;
$c->res->headers->header('X-Accel-Redirect' => '/path/to/document.pdf');
$c->render(data => '', status => 200);
}
But if you need to handle this at the middleware layer instead (lowkey valid, it makes it more transparent β¨), you can replicate the behavior inline using Plack::Builder:
use Plack::Builder;
use Plack::Util;
use Scalar::Util qw(blessed);
builder {
enable sub {
my $app = shift;
sub {
my $env = shift;
my $res = $app->($env);
Plack::Util::response_cb($res, sub {
my $res = shift;
my $body = $res->[2];
if (blessed($body) && $body->can('path')) {
my $h = Plack::Util::headers($res->[1]);
$h->set('X-Sendfile' => $body->path);
$h->set('Content-Length', 0);
$res->[2] = [];
}
});
};
};
$app;
};
π DESCRIPTION π
So here's the tea β β when the body is a blessed reference with a path method, then the return value of that method is what gets used to set the X-Sendfile header. Chef's kiss π«Ά
Then the body gets set to an empty list, and the Content-Length header gets set to 0.
But wait β if the X-Sendfile header is already set, then the body and Content-Length stay completely untouched. She respects boundaries. π
You should use IO::File::WithPath or Plack::Util's set_io_path to add that path method to an IO object in the body, iykyk π§
For frontend configuration examples, go peep https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile.
One thing tho: Plack::Middleware::XSendfile does not set the Content-Type header. Not her job bestie π
π FRONTEND CONFIGURATION π
πΈ Nginx πΈ
Nginx supports X-Accel-Redirect. So you configure an internal location and pass the X-Accel-Mapping header to the backend so the middleware can rewrite filesystem paths into internal URLs:
location ~ /files/(.*) {
internal;
alias /var/www/$1;
}
location / {
proxy_pass http://127.0.0.1:5000/;
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
proxy_set_header X-Accel-Mapping /var/www/=/files/;
}
And X-Accel-Mapping is the one that tells the middleware which filesystem prefix to replace and what internal URL prefix to use instead. Slay β¨
π Apache π
Enable mod_xsendfile (peep https://tn123.org/mod_xsendfile/) and set the request header so the middleware activates:
RequestHeader Set X-Sendfile-Type X-Sendfile
XSendFile on
π¦ lighttpd π¦
proxy-core.allow-x-sendfile = "enable"
proxy-core.rewrite-request = (
"X-Sendfile-Type" => (".*" => "X-Sendfile")
)
π₯ SECURITY π₯
Bestie. PAY ATTENTION here. This is the whole reason the thing is deprecated. π
This middleware reads X-Sendfile-Type and X-Accel-Mapping from incoming request headers to figure out how to serve files. Which means it is critical that these headers are set by the frontend proxy and cannot be supplied by untrusted clients β otherwise a client could influence which files the frontend serves. Nope nope nope. π
The Plack backend must not be directly reachable by untrusted clients. Periodt.
For each frontend, make sure both headers are explicitly set in the proxy configuration. proxy_set_header (nginx), RequestHeader Set (Apache), and proxy-core.rewrite-request (lighttpd) all overwrite any client-supplied values, which is exactly why the examples above use those directives. That's the whole point π«Ά
π CONFIGURATION π
variation
The header tag to use. If it's unset, the environment key plack.xsendfile.type will be used, and then after that the HTTP_X_SENDFILE_TYPE header.
Supported values are:
X-Accel-RedirectX-Lighttpd-Send-FileX-Sendfile.
An unsupported value will log an error. π
π AUTHOR π
Tatsuhiko Miyagawa
okay that's the whole page bestie, you ate that π β¨
this was perl v5.42.3 and the date on it was 2026-10-18 β that's the version and timestamp from the footer of Plack::...endfile(3), in case anyone asks π
π¦ where this page comes from
- fedora 43: perl-Plack 1.0055-1.fc43 Β· GPL-1.0-or-later OR Artistic-1.0-Perl
- fedora 44: perl-Plack 1.0055-1.fc44 Β· GPL-1.0-or-later OR Artistic-1.0-Perl
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms