{"id":20499701,"url":"https://github.com/apla/node-static-server-test","last_synced_at":"2026-04-21T04:32:25.114Z","repository":{"id":66835392,"uuid":"478987797","full_name":"apla/node-static-server-test","owner":"apla","description":null,"archived":false,"fork":false,"pushed_at":"2022-04-12T19:19:02.000Z","size":41,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-05T19:32:03.623Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/apla.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-07T13:02:42.000Z","updated_at":"2022-12-07T13:23:17.000Z","dependencies_parsed_at":"2023-02-21T13:45:31.631Z","dependency_job_id":null,"html_url":"https://github.com/apla/node-static-server-test","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/apla/node-static-server-test","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apla%2Fnode-static-server-test","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apla%2Fnode-static-server-test/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apla%2Fnode-static-server-test/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apla%2Fnode-static-server-test/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/apla","download_url":"https://codeload.github.com/apla/node-static-server-test/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apla%2Fnode-static-server-test/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32076928,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-21T02:38:07.213Z","status":"ssl_error","status_checked_at":"2026-04-21T02:38:06.559Z","response_time":128,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T18:18:09.343Z","updated_at":"2026-04-21T04:32:25.099Z","avatar_url":"https://github.com/apla.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# node-static-server-test\n\n## Serving static files with Node.JS\n\nNode.js have an internal `http`/`https` module to create a web server.\nBut almost nobody uses `http.Server` as is, the majority of users\nare using `Express.js` either directly or indirectly,\nas a part of a framework like `Next.js`.\n\n[Express.js](https://expressjs.com), with its long-lived version 4\nis famous for its somewhat low performance. Other projects\nlike [fastify](https://www.fastify.io)\nor [polka](https://github.com/lukeed/polka) have benchmarks\noutperforming `Express.js`. I don’t know why `Express` is slower,\nmaybe because of regex processing of routes?\nIf you’re using parametric routes like `/users/:userid/entity`\nand have no regexp routes, then replacing `Express.js`\nwith `fastify` or `polka` will add a performance boost to your app.\nThey are not direct replacements, but you can convert code\nif you really need that boost. In the article below benchmarks\nshows huge improvement, but in reality, your code will be a limiting\nfactor to your app performance, and you are unlikely notice any improvement.\n\nWhile writing this article, I tested many configurations: node http, node http + nginx, node http with unix socket + nginx, nginx keepalive for previous configuration. Even with very short response, protocol overhead not so big to give any performance benefits.\n\nAlong with dynamic content, node.js web servers can, obviously,\nserve static files. Performance-wise, it is not the best way\nto serve static files. Using a separate proxy server\nlike [nginx](https://www.nginx.com) is much better for that purpose.\nLinux systems have several technologies to optimize such tasks.\n`sendfile` allows you to stream file contents to the socket\nusing operating system routines and buffers.\n`mmap` can be used to map file contents to the memory\nand speed up reading purposes. In addition to the system calls above,\nNginx can use its own caching mechanisms. As your project grows,\nyou may use AWS/Azure/Google/Cloudflare/whatever CDNs\nto distribute static files for users in different regions.\nThis way you’re trading the cost of running your compute nodes\nfor cheaper CDN bandwidth.\n\n### Serving static content\n\nLet’s get back to the coding. While you’re writing code for your server,\nit’s probably easier to include static file serving into web server code.\nAnd, probably, this should not affect your server performance. Let’s try!\n\nAll code snippets and test scripts are available on my GitHub repo https://github.com/apla/node-static-server-test.\n\n![node serve static express polka fastify](./node-serve-static-drop.svg)\n\n\u003eServers with only dynamic routes (higher bars) and with added file serving routine (lower).\n\nCode for static file serving adopted from those pages:\n\n * https://expressjs.com/en/starter/static-files.html\n * https://expressjs.com/en/resources/middleware/serve-static.html\n * https://github.com/lukeed/polka/blob/master/examples/with-serve-static/index.js\n * https://github.com/fastify/fastify-static\n\nWhy web server performance suffers from file serving middleware?\nChaining middleware is a way to write asynchronous code the same way\nas old synchronous code was written decades ago. Chained middlewares\ndissect request bit by bit and made those bits available\nbefore starting the main URL handler in the app. But everything\ncomes with a cost. Mapping URLs to the file system, checking session\nfrom cookie against a database, parsing request body, and storing uploaded files\nin the filesystem consume resources. As an application developer,\nyou can choose proper way, when you use middleware as a request\nprocessing atoms depending on URL. Or Lazy way, where most middlewares\nare just generic request parser/validator/something else\nand used like `app.use(middleware)`.\n\nSuch a lazy approach leads to running every application middleware\nbefore processing every request.\n\nAs you can see on the chart, I’ve added file serving middleware\nand they run before request. To send file contents to the user,\nthe serving routine should make sure the file exists. So, for every request\nweb server checks if there is file exists. \n\n### Filesystem callback\n\nBut what do I really want, when I add file serving middleware into my app?\nI want my dynamic routes processed as usual, but, _if none matches_,\nthe server should check for the path in the filesystem. Only as a fallback. \n\n`Express.js` doesn’t have such a handler, but it processes `use` middlewares\nas registered by use method. `polka` calls all `use` middlewares at request start,\nbut have `onNoMatch` handler. `fastify` server page mentions\n[setNotFoundHandler](https://www.fastify.io/docs/latest/Reference/Server/#setnotfoundhandler)\nwith `preValidation` hook on [lifecycle page](https://www.fastify.io/docs/latest/Reference/Lifecycle/#lifecycle).\nBut I could not find a way to use `fastify-static` with `preValidation` hook.\n\nResults:\n\n![node serve static fixed express polka fastify](./node-serve-static-all.svg)\n\n\u003eFile handlers push at the end of middleware list (express)\n\u003eor to special _if none matches_ handler (polka). No solution for fastify.\n\nAs you can see, proper middleware usage can benefit your app with faster\nresponse times and lower system load. Maybe it’s time to check other\n`use`d middlewares and move form validation, body parsing,\nand other specific middlewares to the URLs where is needed?\n\n### Existing static middleware\n\nWhile browsing source files, I discovered some overengineered static handlers:\n\n * https://github.com/expressjs/serve-static/blob/master/index.js\n * https://github.com/fastify/fastify-static/blob/master/index.js\n * https://github.com/lukeed/sirv/blob/master/packages/sirv/index.js\n\nAt least two of them use `send` package\n\nhttps://github.com/expressjs/serve-static/blob/master/index.js\n\n`serve-static` is default for `Express` and `fastify-static` is default for `fastify`;\nthose packages are much slower than a real proxy. They must be used only for testing\nand light load scenarios, but with a light load, you’re not needed\n`ETag`, `Cache-Control` and `Max-Age` headers and other engineering efforts\nto optimize file serving. `sirv` package does even more. It caches file stat in memory, without revalidating\nwhen the file changes. I described why those efforts it is not needed at the beginning\nof this article. You can trust me, or you check it out for yourself.\n\n![node serve static express polka nginx](./node-serve-static-nginx.svg)\n\n\u003eExpress, polka, and Nginx comparison on 1K file. RPS values differ\nfrom previous charts because benchmarks performed on slower Linux VPS.\nI did it on purpose to limit all servers to using only one available CPU core.\n\nBefore writing this article I’ve\n[seen](https://www.reddit.com/r/node/comments/cu74cz/explain_me_serving_static_files_in_express_in_an/)\n[many](https://hashnode.com/post/why-is-it-not-recommended-to-serve-static-files-from-nodejs-ciibz8flv01duj3xt4lxuomp3)\n[questions](https://stackoverflow.com/questions/9967887/node-js-itself-or-nginx-frontend-for-serving-static-files)\nit is good or not to use Node.JS as http file server. And I have\nno definitive answer on how much difference I will have. I always used Nginx\nbefore node.js to serve static in world-facing services. \n\n### More bad examples\n\nTake a look at Nest.js web server. When the file serving option is turned on, it not only slows down your app because [filesystem checks for every request](https://github.com/nestjs/serve-static/blob/master/lib/loaders/express.loader.ts) but also using [synchronous fs.stat](https://github.com/nestjs/serve-static/commit/532ca9047bc40efeb00f5f0aae3ab7f194097c9b) to check if the file exists.\n\n## Conclusion\n\nYou definitely should not have to use node.js for static files in production. And it is better to use that functionality only in development because on every unknown dynamic route your web server will check the filesystem. But the main point of this article is that wrongly placed middleware can hurt the performance of your app.\n\n### P.S.: Best performance at any cost\n\nIf you want the best performance at any cost, take a look at uWebSockers.js.\nThis is very fast web server, developed by Alex Hultman.\n\nOn my benchmark uWebSockets.js can handle 74527.95 requests per second with single process, while cluster of two polka nodes just 63141.36. Additional performance can be squeezed from node `http`, but load balancing is a [known linux problem](https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/).\n\nFile serving doesn't need any workarounds because [of good routes handling](https://github.com/uNetworking/uWebSockets/blob/master/misc/READMORE.md).\n\n\u003ePattern matching\n\u003e\n\u003eRoutes are matched in order of specificity, not by the order you register them:\n\u003e\n\u003eHighest priority - static routes, think \"/hello/this/is/static\".\n\u003eMiddle priority - parameter routes, think \"/candy/:kind\", where \u003evalue of :kind is retrieved by req.getParameter(0).\n\u003eLowest priority - wildcard routes, think \"/hello/*\".\n\u003eIn other words, the more specific a route is, the earlier it will match. This allows you to define wildcard routes that match a wide \u003erange of URLs and then \"carve\" out more specific behavior from that.\n\nBut static serving performance is not so good (10K file):\n\npolka-cluster 17778.46 RPS\nuwf-fixed 9023.0 RPS\n\nI have not added this server to compare because the author has his reasons and way of doing things. For example:\n\n * npm drama: npm wouldn't allow developer to delete previous versions of his package that had bugs and security issues so he got angry and released an empty package with a patch version. npm tagged `latest` latest non-empty package because people complain after suddenly webserver stopper to work. After that, developer deprecated the package ([removed reddit post](https://www.reddit.com/r/node/comments/91kgte/uws_has_been_deprecated/)); https://medium.com/@rockstudillo/beware-of-uwebsockets-js-b51c92cac83f\nhttps://alexhultman.medium.com/beware-of-tin-foil-hattery-f738b620468c\n * [nodejs drama](https://github.com/uNetworking/uWebSockets.js/discussions/171): developer doesn't want to comply with existing nodejs interfaces with it's own nodejs package. «What Node.js does with their streams has no significance to this project. If you see similarities - good - but that doesn't mean anything more than that there are similarities. The entire premise, the hypothesis of this project since day 1 has always been and will continue to be: \"Node.js is doing things unreasonably inefficient.\" In other words - the difference between this project and Node.js is no act of random.»\n * another npm drama: https://github.com/uNetworking/uWebSockets.js/discussions/413\n * Freedom truckers convoy icon on Github profile. Does he support only AntiCovid hysteria or horn punishment for Ottawa citizens too?\n\nTo me, this developer is in the good company of authors of `leftpad`, `event-stream`, `node-ipc`. I don't trust `uWebSockets.js` author and I will never use it in my projects.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapla%2Fnode-static-server-test","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapla%2Fnode-static-server-test","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapla%2Fnode-static-server-test/lists"}