Plugin for serving static files as fast as possible
Installations
npm install @fastify/static
Developer Guide
Typescript
Yes
Module System
CommonJS
Node Version
20.18.0
NPM Version
10.8.2
Score
68.4
Supply Chain
99.2
Quality
94.9
Maintenance
100
Vulnerability
100
License
Releases
Contributors
Languages
JavaScript (95.76%)
TypeScript (3.54%)
HTML (0.59%)
CSS (0.11%)
Developer
Download Statistics
Total Downloads
68,883,482
Last Day
150,260
Last Week
716,613
Last Month
3,182,101
Last Year
42,062,727
GitHub Statistics
455 Stars
481 Commits
105 Forks
19 Watching
5 Branches
81 Contributors
Sponsor this package
Package Meta Information
Latest Version
8.1.0
Package Id
@fastify/static@8.1.0
Unpacked Size
406.40 kB
Size
242.94 kB
File Count
68
NPM Version
10.8.2
Node Version
20.18.0
Publised On
02 Feb 2025
Total Downloads
Cumulative downloads
Total Downloads
68,883,482
Last day
-11%
150,260
Compared to previous day
Last week
-15.1%
716,613
Compared to previous week
Last month
1.7%
3,182,101
Compared to previous month
Last year
93.4%
42,062,727
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
@fastify/static
Plugin for serving static files as fast as possible.
Install
npm i @fastify/static
Compatibility
Plugin version | Fastify version |
---|---|
^8.x | ^5.x |
^7.x | ^4.x |
^5.x | ^3.x |
^2.x | ^2.x |
^1.x | ^1.x |
Please note that if a Fastify version is out of support, then so are the corresponding versions of this plugin in the table above. See Fastify's LTS policy for more details.
Usage
1const fastify = require('fastify')({logger: true}) 2const path = require('node:path') 3 4fastify.register(require('@fastify/static'), { 5 root: path.join(__dirname, 'public'), 6 prefix: '/public/', // optional: default '/' 7 constraints: { host: 'example.com' } // optional: default {} 8}) 9 10fastify.get('/another/path', function (req, reply) { 11 reply.sendFile('myHtml.html') // serving path.join(__dirname, 'public', 'myHtml.html') directly 12}) 13 14fastify.get('/another/patch-async', async function (req, reply) { 15 return reply.sendFile('myHtml.html') 16}) 17 18fastify.get('/path/with/different/root', function (req, reply) { 19 reply.sendFile('myHtml.html', path.join(__dirname, 'build')) // serving a file from a different root location 20}) 21 22fastify.get('/another/path', function (req, reply) { 23 reply.sendFile('myHtml.html', { cacheControl: false }) // overriding the options disabling cache-control headers 24}) 25 26// Run the server! 27fastify.listen({ port: 3000 }, (err, address) => { 28 if (err) throw err 29 // Server is now listening on ${address} 30})
Multiple prefixed roots
1const fastify = require('fastify')() 2const fastifyStatic = require('@fastify/static') 3const path = require('node:path') 4// first plugin 5fastify.register(fastifyStatic, { 6 root: path.join(__dirname, 'public') 7}) 8 9// second plugin 10fastify.register(fastifyStatic, { 11 root: path.join(__dirname, 'node_modules'), 12 prefix: '/node_modules/', 13 decorateReply: false // the reply decorator has been added by the first plugin registration 14}) 15
Sending a file with content-disposition
header
1const fastify = require('fastify')() 2const path = require('node:path') 3 4fastify.register(require('@fastify/static'), { 5 root: path.join(__dirname, 'public'), 6 prefix: '/public/', // optional: default '/' 7}) 8 9fastify.get('/another/path', function (req, reply) { 10 reply.download('myHtml.html', 'custom-filename.html') // sending path.join(__dirname, 'public', 'myHtml.html') directly with custom filename 11}) 12 13fastify.get('another/patch-async', async function (req, reply) { 14 // an async handler must always return the reply object 15 return reply.download('myHtml.html', 'custom-filename.html') 16}) 17 18fastify.get('/path/without/cache/control', function (req, reply) { 19 reply.download('myHtml.html', { cacheControl: false }) // serving a file disabling cache-control headers 20}) 21 22fastify.get('/path/without/cache/control', function (req, reply) { 23 reply.download('myHtml.html', 'custom-filename.html', { cacheControl: false }) 24}) 25
Options
root
(required)
The absolute path of the directory containing the files to serve.
The file to serve is determined by combining req.url
with the
root directory.
An array of directories can be provided to serve multiple static directories under a single prefix. Files are served in a "first found, first served" manner, so list directories in order of priority. Duplicate paths will raise an error.
prefix
Default: '/'
A URL path prefix used to create a virtual mount path for the static directory.
constraints
Default: {}
Constraints to add to registered routes. See Fastify's documentation for route constraints.
logLevel
Default: info
Set log level for registered routes.
prefixAvoidTrailingSlash
Default: false
If false
, the prefix gets a trailing "/". If true
, no trailing "/" is added to the prefix.
schemaHide
Default: true
A flag that defines if the fastify route hide-schema attribute is hidden or not.
setHeaders
Default: undefined
A function to set custom headers on the response. Alterations to the headers
must be done synchronously. The function is called as fn(res, path, stat)
,
with the arguments:
res
The response object.path
The path of the file that is being sent.stat
The stat object of the file that is being sent.
send
Options
The following options are also supported and will be passed directly to the
@fastify/send
module:
acceptRanges
contentType
cacheControl
- Enable or disable setting Cache-Control response header (defaults totrue
). To provide a custom Cache-Control header, set this option to falsedotfiles
etag
extensions
immutable
index
lastModified
maxAge
These options can be altered when calling reply.sendFile('filename.html', options)
or reply.sendFile('filename.html', 'otherfilename.html', options)
on each response.
redirect
Default: false
If set to true
, @fastify/static
redirects to the directory with a trailing slash.
This option cannot be true
if wildcard
is false
and ignoreTrailingSlash
is true
.
If false
, requesting directories without a trailing slash triggers the app's 404 handler using reply.callNotFound()
.
wildcard
Default: true
If true
, @fastify/static
adds a wildcard route to serve files.
If false
, it globs the filesystem for all defined files in the
served folder (${root}/**/**
) and creates the necessary routes,
but will not serve newly added files.
The default options of glob
are applied for getting the file list.
This option cannot be false
if redirect
is true
and ignoreTrailingSlash
is true
.
allowedPath
Default: (pathName, root, request) => true
This function filters served files. Using the request object, complex path authentication is possible.
Returning true
serves the file; returning false
calls Fastify's 404 handler.
index
Default: undefined
Under the hood, @fastify/send
supports "index.html" files by default.
To disable this, set false
, or supply a new index by passing a string or an array in preferred order.
serveDotFiles
Default: false
If true
, serves files in hidden directories (e.g., .foo
).
list
Default: undefined
If set, provides the directory list by calling the directory path. Default response is JSON.
Multi-root is not supported within the list
option.
If dotfiles
is deny
or ignore
, dotfiles are excluded.
Example:
1fastify.register(require('@fastify/static'), { 2 root: path.join(__dirname, 'public'), 3 prefix: '/public/', 4 index: false 5 list: true 6})
Request
1GET /public
Response
1{ "dirs": ["dir1", "dir2"], "files": ["file1.png", "file2.txt"] }
list.format
Default: json
Options: html
, json
Directory list can be in html
format; in that case, list.render
function is required.
This option can be overridden by the URL parameter format
. Options are html
and json
.
1GET /public/assets?format=json
Returns the response as JSON, regardless of list.format
.
Example:
1fastify.register(require('@fastify/static'), { 2 root: path.join(__dirname, 'public'), 3 prefix: '/public/', 4 list: { 5 format: 'html', 6 render: (dirs, files) => { 7 return ` 8<html><body> 9<ul> 10 ${dirs.map(dir => `<li><a href="${dir.href}">${dir.name}</a></li>`).join('\n ')} 11</ul> 12<ul> 13 ${files.map(file => `<li><a href="${file.href}" target="_blank">${file.name}</a></li>`).join('\n ')} 14</ul> 15</body></html> 16` 17 }, 18 } 19})
Request
1GET /public
Response
1<html><body> 2<ul> 3 <li><a href="/dir1">dir1</a></li> 4 <li><a href="/dir1">dir2</a></li> 5</ul> 6<ul> 7 <li><a href="/foo.html" target="_blank">foo.html</a></li> 8 <li><a href="/foobar.html" target="_blank">foobar.html</a></li> 9 <li><a href="/index.css" target="_blank">index.css</a></li> 10 <li><a href="/index.html" target="_blank">index.html</a></li> 11</ul> 12</body></html>
list.names
Default: ['']
Directory list can respond to different routes declared in list.names
.
🛈 Note: If a file with the same name exists, the actual file is sent.
Example:
1fastify.register(require('@fastify/static'), { 2 root: path.join(__dirname, '/static'), 3 prefix: '/public', 4 prefixAvoidTrailingSlash: true, 5 list: { 6 format: 'json', 7 names: ['index', 'index.json', '/'] 8 } 9})
Dir list respond with the same content to:
1GET /public 2GET /public/ 3GET /public/index 4GET /public/index.json
list.extendedFolderInfo
Default: undefined
If true
, extended information for folders will be accessible in list.render
and the JSON response.
1render(dirs, files) { 2 const dir = dirs[0]; 3 dir.fileCount // number of files in this folder 4 dir.totalFileCount // number of files in this folder (recursive) 5 dir.folderCount // number of folders in this folder 6 dir.totalFolderCount // number of folders in this folder (recursive) 7 dir.totalSize // size of all files in this folder (recursive) 8 dir.lastModified // most recent last modified timestamp of all files in this folder (recursive) 9}
âš Warning: This will slightly decrease the performance, especially for deeply nested file structures.
list.jsonFormat
Default: names
Options: names
, extended
Determines the output format when json
is selected.
names
:
1{ 2 "dirs": [ 3 "dir1", 4 "dir2" 5 ], 6 "files": [ 7 "file1.txt", 8 "file2.txt" 9 ] 10}
extended
:
1{ 2 "dirs": [ 3 { 4 "name": "dir1", 5 "stats": { 6 "dev": 2100, 7 "size": 4096 8 }, 9 "extendedInfo": { 10 "fileCount": 4, 11 "totalSize": 51233 12 } 13 } 14 ], 15 "files": [ 16 { 17 "name": "file1.txt", 18 "stats": { 19 "dev": 2200, 20 "size": 554 21 } 22 } 23 ] 24}
preCompressed
Default: false
First, try to send the brotli encoded asset (if supported by Accept-Encoding
headers), then gzip, and finally the original pathname
. Skip compression for smaller files that do not benefit from it.
Assume this structure with the compressed asset as a sibling of the uncompressed counterpart:
./public
├── main.js
├── main.js.br
├── main.js.gz
├── crit.css
├── crit.css.gz
└── index.html
Disable serving
To use only the reply decorator without serving directories, pass { serve: false }
.
This prevents the plugin from serving everything under root
.
Disabling reply decorator
The reply object is decorated with a sendFile
function by default. To disable this,
pass { decorateReply: false }
. If @fastify/static
is registered to multiple prefixes
in the same route, only one can initialize reply decorators.
Handling 404s
If a request matches the URL prefix
but no file is found, Fastify's 404
handler is called. Set a custom 404 handler with fastify.setNotFoundHandler()
.
When registering @fastify/static
within an encapsulated context, the wildcard
option may need to be set to false
to support index resolution and nested not-found-handler:
1const app = require('fastify')(); 2 3app.register((childContext, _, done) => { 4 childContext.register(require('@fastify/static'), { 5 root: path.join(__dirname, 'docs'), // docs is a folder that contains `index.html` and `404.html` 6 wildcard: false 7 }); 8 childContext.setNotFoundHandler((_, reply) => { 9 return reply.code(404).type('text/html').sendFile('404.html'); 10 }); 11 done(); 12}, { prefix: 'docs' });
This code will send the index.html
for the paths docs
, docs/
, and docs/index.html
. For all other docs/<undefined-routes>
it will reply with 404.html
.
Handling Errors
If an error occurs while sending a file, it is passed to Fastify's error handler.
Set a custom handler with fastify.setErrorHandler()
.
Payload stream.path
Access the file path inside the onSend
hook using payload.path
.
1fastify.addHook('onSend', function (req, reply, payload, next) {
2 console.log(payload.path)
3 next()
4})
License
Licensed under MIT.
![Empty State](/_next/static/media/empty.e5fae2e5.png)
No vulnerabilities found.
Reason
27 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
0 existing vulnerabilities detected
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
security policy file detected
Details
- Info: security policy file detected: github.com/fastify/.github/SECURITY.md:1
- Info: Found linked content: github.com/fastify/.github/SECURITY.md:1
- Info: Found disclosure, vulnerability, and/or timelines in security policy: github.com/fastify/.github/SECURITY.md:1
- Info: Found text in security policy: github.com/fastify/.github/SECURITY.md:1
Reason
SAST tool is not run on all commits -- score normalized to 8
Details
- Warn: 21 commits out of 24 are checked with a SAST tool
Reason
Found 6/29 approved changesets -- score normalized to 2
Reason
detected GitHub workflow tokens with excessive permissions
Details
- Warn: no topLevel permission defined: .github/workflows/ci.yml:1
- Info: no jobLevel write permissions found
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Score
6.7
/10
Last Scanned on 2025-01-27
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn MoreOther packages similar to @fastify/static
fastify-static
`fastify-static@4.7.0` has been deprecated. Please use `@fastify/static@5.0.0` instead.
@fastify/send
Better streaming static file server with Range and conditional-GET support
@fastify/swagger
Serve Swagger/OpenAPI documentation for Fastify, supporting dynamic generation
@forrestjs/service-fastify-static
ForrestJS service which adds static content serving to a Fastify server.