7
7
Plugin for serving static files as fast as possible.
8
8
9
9
## Install
10
+
10
11
```
11
12
npm i @fastify/static
12
13
```
13
14
14
15
### Compatibility
15
16
16
17
| Plugin version | Fastify version |
17
- | --------------- | ----------------- |
18
+ | -------------- | --------------- |
18
19
| ` >=8.x ` | ` ^5.x ` |
19
20
| ` ^7.x ` | ` ^4.x ` |
20
21
| ` >=5.x <7.x ` | ` ^3.x ` |
21
22
| ` >=2.x <5.x ` | ` ^2.x ` |
22
23
| ` ^1.x ` | ` ^1.x ` |
23
24
24
-
25
25
Please note that if a Fastify version is out of support, then so are the corresponding versions of this plugin
26
26
in the table above.
27
27
See [ Fastify's LTS policy] ( https://github.com/fastify/fastify/blob/main/docs/Reference/LTS.md ) for more details.
28
28
29
29
## Usage
30
30
31
31
``` js
32
- const fastify = require (' fastify' )({logger: true })
33
- const path = require (' node:path' )
32
+ const fastify = require (" fastify" )({ logger: true });
33
+ const path = require (" node:path" );
34
34
35
- fastify .register (require (' @fastify/static' ), {
36
- root: path .join (__dirname , ' public' ),
37
- prefix: ' /public/' , // optional: default '/'
38
- constraints: { host: ' example.com' } // optional: default {}
39
- })
35
+ fastify .register (require (" @fastify/static" ), {
36
+ root: path .join (__dirname , " public" ),
37
+ prefix: " /public/" , // optional: default '/'
38
+ constraints: { host: " example.com" }, // optional: default {}
39
+ });
40
40
41
- fastify .get (' /another/path' , function (req , reply ) {
42
- reply .sendFile (' myHtml.html' ) // serving path.join(__dirname, 'public', 'myHtml.html') directly
43
- })
41
+ fastify .get (" /another/path" , function (req , reply ) {
42
+ reply .sendFile (" myHtml.html" ); // serving path.join(__dirname, 'public', 'myHtml.html') directly
43
+ });
44
44
45
- fastify .get (' /another/patch-async' , async function (req , reply ) {
46
- return reply .sendFile (' myHtml.html' )
47
- })
45
+ fastify .get (" /another/patch-async" , async function (req , reply ) {
46
+ return reply .sendFile (" myHtml.html" );
47
+ });
48
48
49
- fastify .get (' /path/with/different/root' , function (req , reply ) {
50
- reply .sendFile (' myHtml.html' , path .join (__dirname , ' build' )) // serving a file from a different root location
51
- })
49
+ fastify .get (" /path/with/different/root" , function (req , reply ) {
50
+ reply .sendFile (" myHtml.html" , path .join (__dirname , " build" )); // serving a file from a different root location
51
+ });
52
52
53
- fastify .get (' /another/path' , function (req , reply ) {
54
- reply .sendFile (' myHtml.html' , { cacheControl: false }) // overriding the options disabling cache-control headers
55
- })
53
+ fastify .get (" /another/path" , function (req , reply ) {
54
+ reply .sendFile (" myHtml.html" , { cacheControl: false }); // overriding the options disabling cache-control headers
55
+ });
56
56
57
57
// Run the server!
58
58
fastify .listen ({ port: 3000 }, (err , address ) => {
59
- if (err) throw err
59
+ if (err) throw err;
60
60
// Server is now listening on ${address}
61
- })
61
+ });
62
62
```
63
63
64
64
### Multiple prefixed roots
65
65
66
66
``` js
67
- const fastify = require (' fastify' )()
68
- const fastifyStatic = require (' @fastify/static' )
69
- const path = require (' node:path' )
67
+ const fastify = require (" fastify" )();
68
+ const fastifyStatic = require (" @fastify/static" );
69
+ const path = require (" node:path" );
70
70
// first plugin
71
71
fastify .register (fastifyStatic, {
72
- root: path .join (__dirname , ' public' )
73
- })
72
+ root: path .join (__dirname , " public" ),
73
+ });
74
74
75
75
// second plugin
76
76
fastify .register (fastifyStatic, {
77
- root: path .join (__dirname , ' node_modules' ),
78
- prefix: ' /node_modules/' ,
79
- decorateReply: false // the reply decorator has been added by the first plugin registration
80
- })
81
-
77
+ root: path .join (__dirname , " node_modules" ),
78
+ prefix: " /node_modules/" ,
79
+ decorateReply: false , // the reply decorator has been added by the first plugin registration
80
+ });
82
81
```
83
82
84
83
### Sending a file with ` content-disposition ` header
85
84
86
85
``` js
87
- const fastify = require (' fastify' )()
88
- const path = require (' node:path' )
86
+ const fastify = require (" fastify" )();
87
+ const path = require (" node:path" );
89
88
90
- fastify .register (require (' @fastify/static' ), {
91
- root: path .join (__dirname , ' public' ),
92
- prefix: ' /public/' , // optional: default '/'
93
- })
89
+ fastify .register (require (" @fastify/static" ), {
90
+ root: path .join (__dirname , " public" ),
91
+ prefix: " /public/" , // optional: default '/'
92
+ });
94
93
95
- fastify .get (' /another/path' , function (req , reply ) {
96
- reply .download (' myHtml.html' , ' custom-filename.html' ) // sending path.join(__dirname, 'public', 'myHtml.html') directly with custom filename
97
- })
94
+ fastify .get (" /another/path" , function (req , reply ) {
95
+ reply .download (" myHtml.html" , " custom-filename.html" ); // sending path.join(__dirname, 'public', 'myHtml.html') directly with custom filename
96
+ });
98
97
99
- fastify .get (' another/patch-async' , async function (req , reply ) {
98
+ fastify .get (" another/patch-async" , async function (req , reply ) {
100
99
// an async handler must always return the reply object
101
- return reply .download (' myHtml.html' , ' custom-filename.html' )
102
- })
103
-
104
- fastify .get (' /path/without/cache/control' , function (req , reply ) {
105
- reply .download (' myHtml.html' , { cacheControl: false }) // serving a file disabling cache-control headers
106
- })
107
-
108
- fastify .get (' /path/without/cache/control' , function (req , reply ) {
109
- reply .download (' myHtml.html' , ' custom-filename.html' , { cacheControl: false })
110
- })
111
-
100
+ return reply .download (" myHtml.html" , " custom-filename.html" );
101
+ });
102
+
103
+ fastify .get (" /path/without/cache/control" , function (req , reply ) {
104
+ reply .download (" myHtml.html" , { cacheControl: false }); // serving a file disabling cache-control headers
105
+ });
106
+
107
+ fastify .get (" /path/without/cache/control" , function (req , reply ) {
108
+ reply .download (" myHtml.html" , " custom-filename.html" , {
109
+ cacheControl: false ,
110
+ });
111
+ });
112
112
```
113
113
114
114
### Managing cache-control headers
@@ -117,23 +117,23 @@ Production sites should use a reverse-proxy to manage caching headers.
117
117
However, here is an example of using fastify-static to host a Single Page Application (for example a [ vite.js] ( https://vite.dev/ ) build) with sane caching.
118
118
119
119
``` js
120
- fastify .register (require (' @fastify/static' ), {
121
- root: path .join (import .meta.dirname, ' dist' ), // import.meta.dirname node.js >= v20.11.0
120
+ fastify .register (require (" @fastify/static" ), {
121
+ root: path .join (import .meta.dirname, " dist" ), // import.meta.dirname node.js >= v20.11.0
122
122
// By default all assets are immutable and can be cached for a long period due to cache bursting techniques
123
- maxAge: ' 30d' ,
123
+ maxAge: " 30d" ,
124
124
immutable: true ,
125
- })
125
+ });
126
126
127
127
// Explicitly reduce caching of assets that don't use cache bursting techniques
128
- fastify .get (' / ' , function (req , reply ) {
128
+ fastify .get (" / " , function (req , reply ) {
129
129
// index.html should never be cached
130
- reply .sendFile (' index.html' , {maxAge: 0 , immutable: false })
131
- })
130
+ reply .sendFile (" index.html" , { maxAge: 0 , immutable: false });
131
+ });
132
132
133
- fastify .get (' /favicon.ico' , function (req , reply ) {
133
+ fastify .get (" /favicon.ico" , function (req , reply ) {
134
134
// favicon can be cached for a short period
135
- reply .sendFile (' favicon.ico' , {maxAge: ' 1d ' , immutable: false })
136
- })
135
+ reply .sendFile (" favicon.ico" , { maxAge: " 1d " , immutable: false });
136
+ });
137
137
` ` `
138
138
139
139
### Options
@@ -148,6 +148,12 @@ An array of directories can be provided to serve multiple static directories
148
148
under a single prefix. Files are served in a "first found, first served" manner,
149
149
so list directories in order of priority. Duplicate paths will raise an error.
150
150
151
+ #### ` getPathNotFoundWarning`
152
+
153
+ A custom function that returns a warning message to log when a specified root directory is not found.
154
+ It receives a single argument: the path of the missing root directory.
155
+ The function should return a string describing the missing path.
156
+
151
157
#### ` prefix`
152
158
153
159
Default: ` ' /' `
@@ -275,12 +281,12 @@ If `dotfiles` is `deny` or `ignore`, dotfiles are excluded.
275
281
Example:
276
282
277
283
` ` ` js
278
- fastify .register (require (' @fastify/static' ), {
279
- root: path .join (__dirname , ' public' ),
280
- prefix: ' /public/' ,
284
+ fastify .register (require (" @fastify/static" ), {
285
+ root: path .join (__dirname , " public" ),
286
+ prefix: " /public/" ,
281
287
index: false ,
282
- list: true
283
- })
288
+ list: true ,
289
+ });
284
290
` ` `
285
291
286
292
Request
@@ -314,25 +320,32 @@ Returns the response as JSON, regardless of `list.format`.
314
320
Example:
315
321
316
322
` ` ` js
317
- fastify .register (require (' @fastify/static' ), {
318
- root: path .join (__dirname , ' public' ),
319
- prefix: ' /public/' ,
323
+ fastify .register (require (" @fastify/static" ), {
324
+ root: path .join (__dirname , " public" ),
325
+ prefix: " /public/" ,
320
326
list: {
321
- format: ' html' ,
327
+ format: " html" ,
322
328
render : (dirs , files ) => {
323
329
return `
324
330
<html><body>
325
331
<ul>
326
- ${ dirs .map (dir => ` <li><a href="${ dir .href } ">${ dir .name } </a></li>` ).join (' \n ' )}
332
+ ${ dirs
333
+ .map ((dir ) => ` <li><a href="${ dir .href } ">${ dir .name } </a></li>` )
334
+ .join (" \n " )}
327
335
</ul>
328
336
<ul>
329
- ${ files .map (file => ` <li><a href="${ file .href } " target="_blank">${ file .name } </a></li>` ).join (' \n ' )}
337
+ ${ files
338
+ .map (
339
+ (file ) =>
340
+ ` <li><a href="${ file .href } " target="_blank">${ file .name } </a></li>`
341
+ )
342
+ .join (" \n " )}
330
343
</ul>
331
344
</body></html>
332
- `
333
- },
334
- }
335
- })
345
+ ` ;
346
+ },
347
+ },
348
+ });
336
349
` ` `
337
350
338
351
Request
@@ -344,18 +357,20 @@ GET /public
344
357
Response
345
358
346
359
` ` ` html
347
- < html>< body>
348
- < ul>
349
- < li>< a href= " /dir1" > dir1< / a>< / li>
350
- < li>< a href= " /dir1" > dir2< / a>< / li>
351
- < / ul>
352
- < ul>
353
- < li>< a href= " /foo.html" target= " _blank" > foo .html < / a>< / li>
354
- < li>< a href= " /foobar.html" target= " _blank" > foobar .html < / a>< / li>
355
- < li>< a href= " /index.css" target= " _blank" > index .css < / a>< / li>
356
- < li>< a href= " /index.html" target= " _blank" > index .html < / a>< / li>
357
- < / ul>
358
- < / body>< / html>
360
+ < html>
361
+ < body>
362
+ < ul>
363
+ < li>< a href= " /dir1" > dir1< / a>< / li>
364
+ < li>< a href= " /dir1" > dir2< / a>< / li>
365
+ < / ul>
366
+ < ul>
367
+ < li>< a href= " /foo.html" target= " _blank" > foo .html < / a>< / li>
368
+ < li>< a href= " /foobar.html" target= " _blank" > foobar .html < / a>< / li>
369
+ < li>< a href= " /index.css" target= " _blank" > index .css < / a>< / li>
370
+ < li>< a href= " /index.html" target= " _blank" > index .html < / a>< / li>
371
+ < / ul>
372
+ < / body>
373
+ < / html>
359
374
` ` `
360
375
361
376
#### ` list .names `
@@ -369,15 +384,15 @@ Directory list can respond to different routes declared in `list.names`.
369
384
Example:
370
385
371
386
` ` ` js
372
- fastify .register (require (' @fastify/static' ), {
373
- root: path .join (__dirname , ' /static' ),
374
- prefix: ' /public' ,
387
+ fastify .register (require (" @fastify/static" ), {
388
+ root: path .join (__dirname , " /static" ),
389
+ prefix: " /public" ,
375
390
prefixAvoidTrailingSlash: true ,
376
391
list: {
377
- format: ' json' ,
378
- names: [' index' , ' index.json' , ' / ' ]
379
- }
380
- })
392
+ format: " json" ,
393
+ names: [" index" , " index.json" , " / " ],
394
+ },
395
+ });
381
396
` ` `
382
397
383
398
Dir list respond with the same content to:
@@ -418,20 +433,16 @@ Options: `names`, `extended`
418
433
Determines the output format when ` json` is selected.
419
434
420
435
` names` :
436
+
421
437
` ` ` json
422
438
{
423
- " dirs" : [
424
- " dir1" ,
425
- " dir2"
426
- ],
427
- " files" : [
428
- " file1.txt" ,
429
- " file2.txt"
430
- ]
439
+ " dirs" : [" dir1" , " dir2" ],
440
+ " files" : [" file1.txt" , " file2.txt" ]
431
441
}
432
442
` ` `
433
443
434
444
` extended` :
445
+
435
446
` ` ` json
436
447
{
437
448
" dirs" : [
@@ -496,18 +507,21 @@ handler is called. Set a custom 404 handler with [`fastify.setNotFoundHandler()`
496
507
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:
497
508
498
509
` ` ` js
499
- const app = require (' fastify' )();
510
+ const app = require (" fastify" )();
500
511
501
- app .register ((childContext , _ , done ) => {
502
- childContext .register (require (' @fastify/static' ), {
503
- root: path .join (__dirname , ' docs' ), // docs is a folder that contains `index.html` and `404.html`
504
- wildcard: false
512
+ app .register (
513
+ (childContext , _ , done ) => {
514
+ childContext .register (require (" @fastify/static" ), {
515
+ root: path .join (__dirname , " docs" ), // docs is a folder that contains `index.html` and `404.html`
516
+ wildcard: false ,
505
517
});
506
518
childContext .setNotFoundHandler ((_ , reply ) => {
507
- return reply .code (404 ).type (' text/html' ).sendFile (' 404.html' );
519
+ return reply .code (404 ).type (" text/html" ).sendFile (" 404.html" );
508
520
});
509
521
done ();
510
- }, { prefix: ' docs' });
522
+ },
523
+ { prefix: " docs" }
524
+ );
511
525
` ` `
512
526
513
527
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 ` .
@@ -522,10 +536,10 @@ Set a custom handler with [`fastify.setErrorHandler()`](https://fastify.dev/docs
522
536
Access the file path inside the ` onSend` hook using ` payload .path ` .
523
537
524
538
` ` ` js
525
- fastify .addHook (' onSend' , function (req , reply , payload , next ) {
526
- console .log (payload .path )
527
- next ()
528
- })
539
+ fastify .addHook (" onSend" , function (req , reply , payload , next ) {
540
+ console .log (payload .path );
541
+ next ();
542
+ });
529
543
` ` `
530
544
531
545
## License
0 commit comments