These packages provide essential infrastructure for local web development, ranging from simple static file serving to complex build tool integration. http-server, serve, and live-server focus on serving static assets with varying degrees of configuration. browser-sync and lite-server add live reloading and device syncing capabilities. webpack-dev-server integrates tightly with module bundlers for hot module replacement. nodemon differs by monitoring file changes to restart Node.js processes rather than serving static files directly. Together, they form the backbone of local testing environments.
Choosing the right local server tool impacts your daily development speed, testing accuracy, and deployment confidence. While all seven packages help you run code locally, they solve different problems β from simple file serving to complex module hot-swapping. Let's compare how they handle common engineering tasks.
Live reloading refreshes the browser when files change, while Hot Module Replacement (HMR) updates code without a full refresh. This distinction matters for preserving application state during development.
browser-sync injects a script to reload the browser or sync interactions.
# browser-sync: Watch files and reload
browser-sync start --server "app" --files "*.css, *.html"
lite-server enables live reload out of the box using browser-sync underneath.
// lite-server: Default bs-config.json behavior
{
"server": { "baseDir": "./app" },
"files": ["*.html", "*.css"]
}
live-server watches files and triggers a full page reload.
# live-server: Watch and reload
live-server --port=8080 --watch=css
webpack-dev-server supports true HMR for JavaScript modules.
// webpack-dev-server: webpack.config.js
module.exports = {
devServer: {
hot: true,
static: "./dist"
}
};
http-server does not support live reloading natively.
# http-server: Static only, no reload
npx http-server ./dist
serve does not support live reloading natively.
# serve: Static only, no reload
npx serve ./dist
nodemon restarts Node processes, not browser pages.
# nodemon: Restart server process
nodemon server.js
Some tools work immediately, while others require config files to unlock features. Your choice depends on how much control you need versus how quickly you want to start.
browser-sync offers flexible CLI options or a config file.
// browser-sync: bs-config.js
module.exports = {
server: { baseDir: "./src" },
port: 3000
};
lite-server uses a simplified config file for browser-sync.
// lite-server: bs-config.json
{
"server": { "baseDir": "./public" }
}
live-server relies mostly on CLI flags.
# live-server: CLI flags
live-server --port=3000 --open=/index.html
webpack-dev-server requires a webpack configuration file.
// webpack-dev-server: webpack.config.js
module.exports = {
mode: "development",
devServer: { port: 8080 }
};
http-server requires zero configuration.
# http-server: Run anywhere
npx http-server
serve uses optional flags or a serve.json file.
// serve: serve.json
{
"public": "./build",
"port": 3000
}
nodemon uses nodemon.json or CLI flags.
// nodemon: nodemon.json
{
"ext": "js,json",
"ignore": ["node_modules"]
}
SPAs require the server to return index.html for all routes so the client-side router can handle navigation. Not all static servers support this fallback behavior.
browser-sync supports SPA fallback via configuration.
// browser-sync: Middleware for SPA
middleware: function (req, res, next) {
res.setHeader("Cache-Control", "no-cache");
next();
}
// Requires custom middleware for full SPA fallback
lite-server supports SPA fallback via history API middleware.
// lite-server: Enable history API fallback
{
"server": { "baseDir": "./app", "middleware": { "1": "connect-history-api-fallback" } }
}
live-server does not support SPA routing fallback natively.
# live-server: No built-in SPA fallback
# Returns 404 on refresh for non-root routes
webpack-dev-server handles SPA routing via historyApiFallback.
// webpack-dev-server: Config for SPA
devServer: {
historyApiFallback: true
}
http-server does not support SPA routing fallback.
# http-server: No SPA support
# Will 404 on /about if file doesn't exist
serve supports SPA routing with a specific flag.
# serve: Enable single page app mode
npx serve ./build --single
nodemon does not serve static files directly.
// nodemon: Used with express for SPA
app.get("*", (req, res) => res.sendFile("index.html"));
When your frontend needs to talk to a local API, proxying requests avoids CORS issues. Some tools handle this better than others.
browser-sync can proxy an existing local server.
# browser-sync: Proxy existing server
browser-sync start --proxy "localhost:8000"
lite-server supports proxying via config.
// lite-server: Proxy config
{
"proxy": "http://localhost:8000"
}
live-server has limited proxy support via plugins.
# live-server: Requires custom middleware for proxying
# Not recommended for complex proxy setups
webpack-dev-server supports proxy tables in config.
// webpack-dev-server: Proxy table
devServer: {
proxy: {
"/api": "http://localhost:3000"
}
}
http-server does not support proxying.
# http-server: Static files only
# Cannot proxy API requests
serve does not support proxying.
# serve: Static files only
# Use a separate backend for API
nodemon restarts the backend server itself.
# nodemon: Restart API server
nodemon ./api/server.js
Testing locally in an environment that matches production reduces deployment surprises. Security headers and compression matter here.
browser-sync focuses on development, not production security.
# browser-sync: Dev focused
# No production security headers by default
lite-server inherits browser-sync development focus.
# lite-server: Dev focused
# Not suitable for production hosting
live-server has known security vulnerabilities.
# live-server: Security risks
# Avoid using for sensitive projects
webpack-dev-server is strictly for development.
# webpack-dev-server: Dev only
# Never deploy this server to production
http-server is basic and lacks security headers.
# http-server: Basic serving
# No custom security headers
serve includes security headers and compression.
# serve: Production-like
npx serve ./build --prod
nodemon is for process management, not serving security.
# nodemon: Process monitor
# Security depends on the app it runs
| Feature | browser-sync | http-server | lite-server | live-server | nodemon | serve | webpack-dev-server |
|---|---|---|---|---|---|---|---|
| Live Reload | β Yes | β No | β Yes | β Yes | β Process Only | β No | β HMR |
| SPA Routing | β οΈ Custom | β No | β Yes | β No | β οΈ App Dependent | β --single | β Config |
| Proxying | β Yes | β No | β Yes | β οΈ Limited | β οΈ App Dependent | β No | β Yes |
| Config | Flexible | None | Simple | CLI | Simple | Simple | Complex |
| Best For | Multi-device | Quick Static | Angular/Static | Legacy | Node Backend | Prod Test | Webpack Apps |
serve and http-server are best for testing static builds β choose serve if you need SPA routing or security headers.
browser-sync and lite-server shine when you need live reloading β pick lite-server for simplicity or browser-sync for advanced device syncing.
webpack-dev-server is the standard for complex bundler setups β use it if you need Hot Module Replacement with webpack.
nodemon belongs in your backend toolkit β use it to restart Node APIs, not to serve frontend assets.
live-server should be avoided in new projects β security risks and maintenance gaps make it a liability compared to modern alternatives.
Final Thought: Your local tooling should match your deployment target. If you deploy to a static host, test with serve. If you deploy a bundled app, develop with webpack-dev-server. Avoid mixing development-only tools with production testing to catch issues early.
Choose serve when you need a local server that mimics production behavior, including security headers and SPA routing support. It is excellent for testing build outputs before deployment to ensure your static site behaves correctly in a production-like environment.
Avoid live-server for new projects due to known security vulnerabilities and slower maintenance cycles. If you must use it, restrict it to isolated local environments, but prefer browser-sync or vite for safer, modern alternatives.
Choose browser-sync if you need to test responsive designs across multiple devices simultaneously or require advanced proxying features. It is ideal for teams that need to sync clicks and scrolls across browsers during QA or when working with a existing backend server.
Choose http-server for quick, zero-configuration static file serving when live reload is not required. It is best suited for simple tasks like hosting a build output temporarily or testing static assets without installing heavy dependencies.
Choose lite-server if you want live reloading with minimal configuration, especially for Angular or static projects. It wraps browser-sync with sensible defaults, making it a good choice for developers who want sync features without managing complex config files.
Choose nodemon when developing Node.js backend services or build scripts that need to restart on file changes. It is not a static server itself, so pair it with a server package if you need to serve HTML or assets alongside your API.
Choose webpack-dev-server if your project relies heavily on webpack for bundling and you need Hot Module Replacement (HMR). It is the standard choice for complex frontend applications using webpack, offering deep integration with loaders and plugins.

serve helps you serve a static site, single page application or just a static file (no matter if on your device or on the local network). It also provides a neat interface for listing the directory's contents:

Once it's time to push your site to production, we recommend using Vercel.
servev14 onwards requires Node v14 to run. Please useservev13 if you cannot upgrade to Node v14.
The quickest way to get started is to just run npx serve in your project's directory.
If you prefer, you can also install the package globally (you'll need at least Node LTS):
> npm install --global serve
Once that's done, you can run this command inside your project's directory...
> serve
...or specify which folder you want to serve:
> serve folder-name/
Finally, run this command to see a list of all available options:
> serve --help
Now you understand how the package works! :tada:
To customize serve's behavior, create a serve.json file in the public folder and insert any of these properties.
The core of serve is serve-handler, which can be used as middleware in existing HTTP servers:
const handler = require('serve-handler');
const http = require('http');
const server = http.createServer((request, response) => {
// You pass two more arguments for config and middleware
// More details here: https://github.com/vercel/serve-handler#options
return handler(request, response);
});
server.listen(3000, () => {
console.log('Running at http://localhost:3000');
});
You can also replace
http.createServerwithmicro.
If you want a feature to be added, or wish to report a bug, please open an issue here.
If you wish to contribute to the project, please read the contributing guide first.
This project used to be called list and micro-list. But thanks to TJ Holowaychuk handing us the new name, it's now called serve (which is much more definite).
Leo Lamprecht (@leo)