Static Files
Most apps need to serve some plain files: a stylesheet, a few images, a client-side script. Fast handles this with files(), which maps a URL prefix to a directory on disk and takes care of all the fiddly HTTP details for you.
Serving a directory
Register a static mount before serve(), just like a route:
files('/assets', __DIR__ . '/public/assets', ['maxAge' => 3600]);
Now a request for /assets/app.css streams the file at public/assets/app.css from disk. Fast fills in the right content type, sets up caching headers, and streams the bytes without loading the whole file into memory.
What you get for free
files() is more than a readfile(). It handles the parts of static serving that are easy to get wrong:
- Correct content types, inferred from the file extension.
- ETag and Last-Modified headers, so browsers can revalidate cheaply.
- Conditional requests, returning
304 Not Modifiedwhen appropriate. - Range requests, so media can seek and downloads can resume.
- Disk streaming, so large files never balloon your worker's memory.
The maxAge option sets the Cache-Control max-age in seconds. Tune it to how often your assets change.
Keep static roots clean
This is the important part. A static mount exposes everything under the directory you give it, so that directory must contain only files you are happy to serve publicly.
Never point
files()at a directory that holds source code, configuration, your.env.php, or database files. Give it a dedicated assets folder that contains nothing sensitive, and make sure untrusted users cannot write into it.
A clean layout makes this easy: put browser-facing files in something like public/assets, and keep the rest of your project well outside it.
Static mounts are just routes
Under the hood, a static mount is registered in the same routing trie as your normal routes, so an unmatched static path costs nothing. You can have several mounts for different prefixes, and they coexist with your dynamic routes without any special ordering.
files('/css', __DIR__ . '/public/css', ['maxAge' => 86400]);
files('/js', __DIR__ . '/public/js', ['maxAge' => 86400]);
files('/img', __DIR__ . '/public/img', ['maxAge' => 604800]);
That wraps up the core request-handling surface. Next we move into data, starting with the Database.