diff --git a/README.md b/README.md index 47e81820..e0b0c9d8 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ frankenphp php-server * [Worker mode](https://frankenphp.dev/docs/worker/) * [Early Hints support (103 HTTP status code)](https://frankenphp.dev/docs/early-hints/) * [Real-time](https://frankenphp.dev/docs/mercure/) +* [Efficiently Serving Large Static Files](https://frankenphp.dev/docs/x-sendfile/) * [Configuration](https://frankenphp.dev/docs/config/) * [Docker images](https://frankenphp.dev/docs/docker/) * [Deploy in production](https://frankenphp.dev/docs/production/) diff --git a/caddy/frankenphp/Caddyfile b/caddy/frankenphp/Caddyfile index 60de6f07..2da634b0 100644 --- a/caddy/frankenphp/Caddyfile +++ b/caddy/frankenphp/Caddyfile @@ -19,7 +19,7 @@ # } #} - root * public/ + root public/ encode zstd br gzip # Uncomment the following lines to enable Mercure and Vulcain modules diff --git a/docs/fr/x-sendfile.md b/docs/fr/x-sendfile.md new file mode 100644 index 00000000..ccd42d5d --- /dev/null +++ b/docs/fr/x-sendfile.md @@ -0,0 +1,71 @@ +# Servir efficacement les gros fichiers statiques (`X-Sendfile`/`X-Accel-Redirect`) + +Habituellement, les fichiers statiques peuvent être servis directement par le serveur web, +mais parfois, il est nécessaire d'exécuter du code PHP avant de les envoyer : +contrôle d'accès, statistiques, en-têtes HTTP personnalisés... + +Malheureusement, utiliser PHP pour servir de gros fichiers statiques est inefficace comparé à +à l'utilisation directe du serveur web (surcharge mémoire, diminution des performances...). + +FrankenPHP permet de déléguer l'envoi des fichiers statiques au serveur web +**après** avoir exécuté du code PHP personnalisé. + +Pour ce faire, votre application PHP n'a qu'à définir un en-tête HTTP personnalisé +contenant le chemin du fichier à servir. FrankenPHP se chargera du reste. + +Cette fonctionnalité est connue sous le nom de **`X-Sendfile`** pour Apache, et **`X-Accel-Redirect`** pour NGINX. + +Dans les exemples suivants, nous supposons que le "document root" du projet est le répertoire `public/` +et que nous voulons utiliser PHP pour servir des fichiers stockés en dehors du dossier `public/`, +depuis un répertoire nommé `private-files/`. + +## Configuration + +Tout d'abord, ajoutez la configuration suivante à votre `Caddyfile` pour activer cette fonctionnalité : + +```patch + root public/ + # ... + ++ # Needed for Symfony, Laravel and other projects using the Symfony HttpFoundation component ++ request_header X-Sendfile-Type x-accel-redirect ++ request_header X-Accel-Mapping ../private-files=/private-files ++ ++ intercept { ++ @accel header X-Accel-Redirect * ++ handle_response @accel { ++ root private-files/ ++ rewrite * {resp.header.X-Accel-Redirect} ++ method * GET ++ ++ # Remove the X-Accel-Redirect header set by PHP for increased security ++ header -X-Accel-Redirect ++ ++ file_server ++ } ++ } + + php_server +``` + +## PHP simple + +Définissez le chemin relatif du fichier (à partir de `private-files/`) comme valeur de l'en-tête `X-Accel-Redirect` : + +```php +header('X-Accel-Redirect: file.txt') ; +``` + +## Projets utilisant le composant Symfony HttpFoundation (Symfony, Laravel, Drupal...) + +Symfony HttpFoundation [supporte nativement cette fonctionnalité](https://symfony.com/doc/current/components/http_foundation.html#serving-files). +Il va automatiquement déterminer la bonne valeur pour l'en-tête `X-Accel-Redirect` et l'ajoutera à la réponse. + +```php +use Symfony\Component\HttpFoundation\BinaryFileResponse; + +BinaryFileResponse::trustXSendfileTypeHeader(); +$response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt'); + +// ... +``` diff --git a/docs/x-sendfile.md b/docs/x-sendfile.md new file mode 100644 index 00000000..8017f3d5 --- /dev/null +++ b/docs/x-sendfile.md @@ -0,0 +1,71 @@ +# Efficiently Serving Large Static Files (`X-Sendfile`/`X-Accel-Redirect`) + +Usually, static files can be served directly by the web server, +but sometimes it's necessary to execute some PHP code before sending them: +access control, statistics, custom HTTP headers... + +Unfortunately, using PHP to serve large static files is inefficient compared to +direct use of the web server (memory overload, reduced performance...). + +FrankenPHP lets you delegate the sending of static files to the web server +**after** executing customized PHP code. + +To do this, your PHP application simply needs to define a custom HTTP header +containing the path of the file to be served. FrankenPHP takes care of the rest. + +This feature is known as **`X-Sendfile`** for Apache, and **`X-Accel-Redirect`** for NGINX. + +In the following examples, we assume that the document root of the project is the `public/` directory. +and that we want to use PHP to serve files stored outside the `public/` directory, +from a directory named `private-files/`. + +## Configuration + +First, add the following configuration to your `Caddyfile` to enable this feature: + +```patch + root public/ + # ... + ++ # Needed for Symfony, Laravel and other projects using the Symfony HttpFoundation component ++ request_header X-Sendfile-Type x-accel-redirect ++ request_header X-Accel-Mapping ../private-files=/private-files ++ ++ intercept { ++ @accel header X-Accel-Redirect * ++ handle_response @accel { ++ root private-files/ ++ rewrite * {resp.header.X-Accel-Redirect} ++ method * GET ++ ++ # Remove the X-Accel-Redirect header set by PHP for increased security ++ header -X-Accel-Redirect ++ ++ file_server ++ } ++ } + + php_server +``` + +## Plain PHP + +Set the relative file path (from `private-files/`) as the value of the `X-Accel-Redirect` header: + +```php +header('X-Accel-Redirect: file.txt'); +``` + +## Projects using the Symfony HttpFoundation component (Symfony, Laravel, Drupal...) + +Symfony HttpFoundation [natively supports this feature](https://symfony.com/doc/current/components/http_foundation.html#serving-files). +It will automatically determine the correct value for the `X-Accel-Redirect` header and add it to the response. + +```php +use Symfony\Component\HttpFoundation\BinaryFileResponse; + +BinaryFileResponse::trustXSendfileTypeHeader(); +$response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt'); + +// ... +```