File delivery
Once a file passed the access check, it still has to be transferred to the client. By default, the extension does that in PHP. This chapter explains why this may become a bottleneck on busy websites and how to hand the transfer over to the web server instead.
Why PHP delivery can become a bottleneck
With the rewrite rules from the administrator manual,
every request to /fileadmin/ (except for the "_processed_" folder) is
answered by TYPO3. This includes images embedded in pages and files without any
restriction.
When PHP streams a file, the PHP worker (e.g., a PHP-FPM child process) remains busy for the whole transfer, not only for the access check. As soon as the client reads slower than the server writes, PHP waits for the network. A 20 MB PDF downloaded by a mobile client at 1 Mbit/s keeps one worker busy for about 160 seconds, while using almost no CPU.
The number of workers is limited (pm.). A few slow downloads
are enough to make regular page requests wait in the PHP-FPM listen queue. This
does not show up as CPU or memory load: watch the active processes and the
listen queue on the PHP-FPM status page instead.
Long transfers may also be terminated by limits such as PHP-FPM's
request_, regardless of PHP's own time limit.
Delivery modes
The extension configuration option File delivery (delivery)
selects who transfers the file:
php(default)- The file is streamed by PHP. This works everywhere and needs no web server configuration.
x-sendfile - PHP answers with an empty body and an
X-header holding the absolute path of the file. The web server then transfers the file itself, e.g., Apache with mod_xsendfile. The PHP worker is released as soon as the access check is done.Sendfile x-accel- redirect - Same principle for Nginx, using its built-in
X-header and an internal location. No additional module is needed.Accel- Redirect
In every mode, the access check stays in TYPO3 and the response carries a
Last- header. Conditional requests (If-) for an
unchanged file are answered with 304 Not Modified by the extension itself,
before any transfer is handed over. With x- and
x-, the web server also takes care of byte ranges
(206 Partial Content), so interrupted downloads can be resumed.
Warning
Only switch to x- or x- once the web server
is configured accordingly. Otherwise, the header is not processed and
clients receive an empty file.
Apache with mod_xsendfile
Install and enable the module, e.g., on Debian or Ubuntu:
apt install libapache2-mod-xsendfile
a2enmod xsendfile
Then allow it in your virtual host, for the directories of your storages:
XSendFile On
XSendFilePath /var/www/example.com/public/fileadmin
Please note:
XSendis only allowed in the server or virtual host configuration, not inFile Path .htaccess.- The extension sends the resolved path of the file. mod_xsendfile only serves
it if it lies within
XSendafter resolving symbolic links as well. IfFile Path fileadmin/is a symbolic link, e.g., to theshared/directory of a deployment tool,XSendmust name the resolved directory, not the path of the current release.File Path - Headers set by TYPO3 (
Content-,Type Cache-, ...) are kept.Control
Nginx with X-Accel-Redirect
Add an internal location that maps to the public directory of your TYPO3
installation. Its path must match the extension configuration option
Internal location (delivery_, default:
/_):
location ^~ /_fal_protect_internal/ {
internal;
alias /var/www/example.com/public/;
}
The internal directive ensures that clients cannot request this location
directly. Files of storages outside of the public directory are still streamed
by PHP.
Tip
Nginx evaluates regular expression locations before plain prefix locations.
If your server block already contains regular expression locations matching
/fileadmin/ (e.g., to set headers for PDF files), these win over the
rewrite to index. and files are served without any access check.
A ^~ prefix location avoids that:
location ^~ /fileadmin/ {
location ^~ /fileadmin/_processed_/ {
}
rewrite ^ /index.php last;
}
Verifying the setup
First, make sure a protected file can be downloaded completely and that the
response contains neither X- nor X-, as the web
server consumes these headers:
curl -sI https://example.com/fileadmin/some/document.pdf
Then check that the PHP worker is released. Download a large file at a low rate and watch the PHP-FPM status page in the meantime:
curl -o /dev/null --limit-rate 50k https://example.com/fileadmin/some/large.pdf
With PHP delivery, one worker stays active until the download is finished.
With x- or x-, it is idle again right away.