Plack::Middleware::XSendfile(3pm)

NAME
Plack::Middleware::XSendfile - Sets X-Sendfile (or a like) header for frontends

SYNOPSIS
  enable "Plack::Middleware::XSendfile";

DEPRECATION NOTICE
This middleware is deprecated and will be removed in a future release, due to poor security design caused by the way configuration is passed via HTTP request headers. See "SECURITY".

The simplest replacement is to set the appropriate header directly in your application when 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);
  }

If you need to handle this at the middleware layer instead to make 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
When the body is a blessed reference with a "path" method, then the return value of that method is used to set the X-Sendfile header.

The body is set to an empty list, and the Content-Length header is set to 0.

If the X-Sendfile header is already set, then the body and Content-Length will be untouched.

You should use IO::File::WithPath or Plack::Util's "set_io_path" to add "path" method to an IO object in the body.

See <https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile> for frontend configuration examples.

Plack::Middleware::XSendfile does not set the Content-Type header.

FRONTEND CONFIGURATION

  Nginx
Nginx supports "X-Accel-Redirect". 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/;
  }

"X-Accel-Mapping" tells the middleware which filesystem prefix to replace and what internal URL prefix to use instead.

  Apache
Enable mod_xsendfile (<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
This middleware reads "X-Sendfile-Type" and "X-Accel-Mapping" from incoming request headers to determine how to serve files. It is therefore 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.

The Plack backend must not be directly reachable by untrusted clients.

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 why the examples above use those directives.

CONFIGURATION

variation
    The header tag to use. If unset, the environment key "plack.xsendfile.type" will be used, then the "HTTP_X_SENDFILE_TYPE" header.

    Supported values are:

    •   "X-Accel-Redirect"

    •   "X-Lighttpd-Send-File"

    •   "X-Sendfile".

    An unsupported value will log an error.

AUTHOR
Tatsuhiko Miyagawa