HTTP/2 webserver for static files


5.2.04 years ago4 years agoMinified + gzip package size for @http2/server in KB


@http2/server πŸ•Έ

Static HTTP/2 webserver for single page apps and progressive web apps.

  • HTTP/2, HTTP/1.1, and HTTP/1.0
  • Content encoding for Brotli and Gzip compression
  • Server Push Manifests & Cache Digest
  • Scalable, multi-processor clustering
  • Auto-generate HTTPS certificate for localhost development
  • Fallback for client side routing
  • HTTP to HTTPS redirect
  • CORS
  • Immutable caching of revved files


npm i -g @http2/server


http2server <command> [options]

--version, -v

Display the current version number.

--help [command], -h [command]

List global or command-specific options.

Command: start

Start the server as a long running process that spawns workers.

Aliases: run, up, launch


The environment variable PORT sets the network port for incoming HTTPS connections. This overrides https.port in the configuration file.

The unencrypted HTTP port, used for redirects, is derived by setting the last three digits set to 080. E.g. HTTPS 8443 -> HTTP 8080, HTTPS 443 -> HTTP 80.

--options [file]

Configuration file path. Defaults to: ./http2(|server|live)(.conf(ig)).js(on)

If no configuration file is found, the default configuration is. See the Configuration section for details.


Defaults to false, which is the same as specifying the --no-watch option.

The server maintains an in-memory index of all files. Compared to dynamic filesystem scanning, this dramatically improves performance when pushing many small files. However if files and directories are added, moved, or deleted, the index becomes incorrect. Pass the --watch option to monitor filesystem changes and automatically rebuild the index. This has some performance cost, depending on operating system specific support, so watching is disabled by default. Watching is typically only used in development mode, not production.

Command: stop

Shuts down the server by sending it the SIGINT signal.

Aliases: halt, exit, quit, kill, down, end

Command: reload

Gracefully restart workers with a new configuration by sending the server the SIGHUP signal.

Aliases: restart, refresh, renew, update, cycle, load

Command: test

Loads and validates a server configuration.

Aliases: verify, validate, check, configtest, lint

--config [file]

Identical to the same option of the start command.


If the server is started from the command line, it can be controlled by sending process signals.


Graceful shutdown of all workers and the master process.


Gracefully restart all workers with reloaded configurations.

JavaScript API


const {Master} = require('http2/server')
const server = new Master(configuration)

The configuration argument contains:

  • options: The server configuration object.
  • generateCertificate: Defaults to false. If true, a self-signed, trusted certificate for DNS:localhost is created on startup in case https.key and https.cert are missing.
  • serveDefaultSite: Defaults to true. Creates a fallback host configuration using default settings if no hosts are explicitly configured. This is a convenience feature for CLI usage so that no configuration is needed by default.
  • cwd: Defaults to the current working directory (./). File and directory paths in the configuration are relative to this path.


await server.listen()

Loads the configuration and spawns worker processes to listen for incoming connections. Also starts a redirecting server from HTTP to HTTPS.


await server.close()

Shuts down the workers and redirecting server gracefully.


await server.reload()

Starts new workers to gracefully replace the old ones without downtime. Reloads the configuration.

Run-Time Updates

await server.message(message)

Invalidates cached information about files, certificates, and configuration on a per-domain level. Used to update at run-time without the performance cost of a full reload. See Messaging for details.

Messaging API

Messages are used to notify the workers of changes without reloading them. This is more efficient than spawning workers.

Messages can be sent programmatically, using the .message(...) method. Messages are also sent by the CLI (i.e. reload, watch, stop commands) and the edge agent which receives them from the core api service.

All messages require at least the type, domain, and root properties.

  • type identifies what kind of information is described. The supported types are defined below.
  • domain is the host name of the site to which this message applies.
  • root is the path of base directory containing the site's files.

Additionally, the configuration field contains a valid configuration document. Only the host options matching the domain are applied, others are ignored.

For example:

  type: 'site-deploy',
  domain: 'example.net',
  root: '/var/www/html',
  configuration: [{
    domain: 'example.net',
    fallback: {200: '/index.html'}

Type: site-deploy

  • isNewDomain Set to true if this domain name was not previously served. Otherwise false. Not currently used.
  • hasNewFiles Set to true if any files are changed, added, or removed. Otherwise false.
  • hasNewConfiguration Set to true if any option in the site configuration is modified.
  type: 'site-deploy',
  domain: 'example.net',
  isNewDomain, // boolean
  hasNewFiles, // boolean
  hasNewConfiguration // boolean

Type: site-delete

A site has been removed. Clear any cached information for the domain.

  type: 'site-delete',
  domain: 'example.net'

Type: configuration-update

New configuration options are used for the domain.

  type: 'configuration-update',
  domain: 'example.net'

Type: file-delete

One or more files have been removed. Clear their cached information.

  type: 'file-delete',
  domain: 'example.net',
  files: [
    {path: 'foo/bar.lol'},
    {path: 'abc/def/ghi.jkl'},
    {path: '123.xyz'}

Type: file-update

One or more files have been changed. Reset or reload their cached information.

  type: 'file-update',
  domain: 'example.net',
  files: [
    {path: 'foo/bar.lol'},
    {path: 'abc/def/ghi.jkl'},
    {path: '123.xyz'}

Type: certificate-issue

A new certificate for the domain is available. Clear any previously cached certificate.

  type: 'certificate-issue',
  domain: 'example.net'

Type: certificate-revoke

The previous certificate for the domain is no longer in use. Clear any cached information.

  type: 'certificate-revoke',
  domain: 'example.net'


See @http2/configuration for the JSON Schema, validation, and normalisation tools.

There are three layers in the configuration:

  • Server Options: applies globally to all sites on the server
    • Host Options: applies to a single domain
      • HTTP/2 Server Push Manifest: also applies to a single domain, but defined as a separate specification

Server Options


Default: true

Boolean value that adds the Server: header to all HTTP responses if true, and omits that header if false.

The value of the header identifies the versions of the server, Node.js, nghttp2, and the operating system.

Often disabled for reasons of paranoia or placebo performance tuning.


Settings related to the Automated Certificate Management Environment (ACME) protocol, i.e. support for the LetsEncrypt certificate authority. This affects all requests, across all hosted domains, where the pathname starts with /.well-known/acme-challenge/. There are many ways to implement ACME support. These settings offer flexibility in issuing certificates and serving the correct certificate for each host.

When a certificate is requested for a domain using the ACME HTTP challenge, the certificate authority (i.e. LetsEncrypt's Boulder server) resolves the given DNS hostname and makes an HTTP request. The webserver needs to respond with values only known by the entity that requested the certificate. In a simple case of one webserver this can be handled by serving a local directory through acme.webroot. But when DNS records point to multiple edge servers, it is necessary to relay or deflect the challenge request, via acme.proxy or acme.redirect respectively, to the certificate requesting entity.

Any domains that do not have a certificate, as per acme.key and acme.cert lookups, are served using the fallback certificate in https.key and https.cert.


Default: ''

If a valid URI is specified, the server proxies all ACME challenge requests to this upstream server.

Proxying requires the server to do more work than a redirect, but it is transparent to the CA. This allows use of ports other than 80 and 443, to which Boulder is restricted.

This string is prepended to the challenge request's URL path, so omit any trailing shash to avoid duplication. Both HTTP and HTTPS can be used, depending on the scheme (http: vs https:).


  acme: {
    proxy: 'https://vault.example.net:8443'


Default: ''

If a valid URI is specified, the server redirects, through a 301 Permanent Redirect response, all ACME challenge requests to this upstream server.

Redirecting is light on the edge server, but requires a second connection from Boulder to the specified server, possibly adding some latency. Not that Boulder, the LetsEncrypt server, is currently restricted to ports 80 and 443. Redirecting to any other ports is not supported, and should be handled through acme.proxy.

This string is prepended to the challenge request's URL path, so omit any trailing shash to avoid duplication. Both HTTP and HTTPS can be used, depending on the scheme, i.e. http: or https: respectively.


  acme: {
    redirect: 'https://vault.example.net:8443'


Default: ''

If a valid path is specified, the server responds to challenges by mapping to static files in this directory. This is intended to work with the webroot option of many ACME clients, where files are generated during the challenge-response process.

  acme: {
    webroot: '/var/www/html'


Default: ''

Directory where keys and certificates are stored.


  acme: {
    webroot: '/etc/ssl'


Default: '$store/$domain/key.pem'

Path to the domain-specific key file.

May contain substitution variables $store and $domain.


  acme: {
    key: '$store/sites/$domain/crypto/key.pem'


Default: '$store/$domain/cert.pem'

Path to the domain-specific certificate file.

May contain substitution variables $store and $domain.


  acme: {
    cert: '$store/sites/$domain/crypto/cert.pem'


Settings for the Pino-based logger.


Default: 'info'

Minimum threshold for log messages to be recorded. One of fatal, error, warn, info, debug, trace; or silent to disable logging.


An object containing settings for the automatic redirection of HTTP to HTTPS. This is necessary since HTTP/2 is only supported by browsers when using a TLS connection (TLS).


Default: true

If true, an HTTP server listens to redirect requests to HTTPS.

If false, no HTTP server is started. This also means ACME is disabled, since it is served over HTTP as well.


Default: 8080

The port number where the HTTP server accepts connections.


Default: 8443

The port number of the HTTPS URLs to which HTTP traffic is redirected.

Redirects use the 308 Permanent Redirect status code.


Settings for the fallback TLS context that is used if no files match acme.key and acme.cert.

Useful when serving subdomains using a wildcard certificate.

If no fallback exists on launch, a self-signed certificate and key pair is generated using tls-keygen. The certificate is attempted to be added to to the operating system trusted store, which may require user confirmation and entry of their password. This is only done in CLI mode, or if the generateCertificate API argument is true; otherwise the server simply fails to launch.

See the corresponding options of tls.createSecureContext.


Default: '~/.http2server/key.pem'

A string of the path to a PEM file containing the secret key.


Default: '~/.http2server/cert.pem'

A string of the path to a PEM file containing the public certificate.


Default: []

An array of strings containing the path to all files in the certificate chain.


Default: 8443

The TCP/IP port number for incoming TLS connections.

The standard HTTPS port is 443 for both HTTP/1.1 and HTTP/2. However, on Unix (Linux/MacOS), root permissions are required to listen on port numbers from 1 to 1024. The default value is outside this restricted range.


The server runs one master process that controls one or more worker processes. The workers process incoming requests and generate responses. Communication between the master and workers uses Node.js cluster module message passing.


Default: 'max_physical_cpu_cores'

The desired number of worker processes to spawn at launch. Must be a number or a mathematical expression that evaluates to a number.


Single worker process:

workers: {
  count: 1

Invalid: must spawn at least 1 worker.

workers: {
  count: 0

One worker per CPU core:

workers: {
  count: 'max_physical_cpu_cores'

Half as many workers as CPU cores:

workers: {
  count: 'max_physical_cpu_cores / 2'

Leave one core for other processes:

workers: {
  count: 'max_physical_cpu_cores - 1'


Default: []

An array of objects containing host options configurations.

Host Options


Default: '' or 'localhost' (only for the default site, see serveDefaultSite)

The DNS hostname of the site. May be specified as an Internationalized Domain Name (IDN) containing non-ASCII characters.


  hosts: [
    { domain: 'example.net' },
    { domain: 'xn--yfro4i67o.example.net' },
    { domain: 'πŸ¦„.example.net' }


The path to the base directory containing static files to serve.

If no root is specified, the server tries to auto-detect static site generator or packaging tool output directories. For example: ./dist, ./public, ./_site, and many more.


Default: {} (no fallback)

An object mapping HTTP status code to file paths.

Fallbacks are supported for:

  • 200 to serve a missing file as a success response. Typically used for client side routing.
  • 404 to serve a missing file as an error response. Typically used for client side routing.


  fallback: {
    200: './index.html'


Sets the HTTP caching headers.


Default: []

An array of path globs for immutable files.

Special named patterns can be used as shorthand. These are:

  • hex β€” Matches hexadecimal hash revved files. Example: layout-d41d8cd98f.css
  • emoji β€” Matches emoji revved files. Example: app.⚽️.js

By default, all non-immutable responses have the header:

cache-control: public, max-age=1, must-revalidate

File paths that match the patterns set by the cacheControl.immutable option are considered to never, ever change their contents. To tell browsers never to revalidate these resources, they are served with the header:

cache-control: public, max-age=31536000, immutable


  cacheControl: {
    immutable: [


Behaviour of directory paths.


Default: 'always'

Enforces a consistent clean URL for directory paths.

The value is a string that can be:

  • 'always' to use HTTP redirects to append a / at the end of the directory name.
  • 'never' to use HTTP redirects to strip any / at the end of the directory name.


Settings related to CORS.


Default: '*'

If specified, sets this value as the access-control-allow-origin header on every response.


Settings related to service workers.


Default: '/'

If specified, sets this value as the service-worker-allowed header on every response.


Settings related to HTTP Strict Transport Security.


Default: 5184000 (60 days)

Sets this value as the strict-transport-security header on every response.


The manifest declares which files or URLs need to be pushed as dependencies for any request.

See the HTTP/2 Server Push Manifest specification for details and examples.

Example: Push all CSS and JS files when serving the homepage.

  hosts: [
    domain: 'localhost',
    root: './dist',
    manifest: [
        get: '**/*.html',
        push: ['**/*.css', '**/*.js']


Server Push with Cache Digests

Page load time is a largely function of latency (round trip time Γ— delays) and aggregate volume (number Γ— size of assets).

Latency is minimised by using HTTP/2 Server Push to deliver any necessary assets to the browser alongside the HTML. When the browser parses the HTML it does not need to make a round trip request to fetch styles, scripts, and other assets. They are already in its cache or actively being pushed by the server as quickly as network conditions allow.

Volume is reduced by using strong compression (HPACK, Brotli, etc), and by avoiding sending redundant data.

If all assets were pushed every time, a large amount of bandwidth would be wasted. HTTP/1 asset concatenation makes a tradeoff between reducing round trips (good) and re-transferring invalidated, large files (bad). For example having to re-tranfer an entire spritesheet or JavaScript bundle because of one little change.

The HTTP/1 approach was to use file signatures (Etags) and timestamps to invalidate cached responses. This requires many expensive round trips where the browser checks with the server if any files have been modified.

Cache Digests to the rescue! Using a clever technique, called Golomb-Rice Coded Bloom Filters, a compressed list of cached responses is sent by the browser to the server. Now the server can avoid pushing assets that are fresh in the browser's cache.

With Server Push and Cache Digests the best practice is to have many small files that can be cached and updated atomically, instead of large, concatenated blobs.

Browsers do not yet support cache digests natively so Service Workers and the Cache API are used to implement them. A cookie-based fallback is available for browsers that lack Service Worker support.

HTTPS Certificate

While the HTTP/2 specification allows unencrypted connections, web browsers strictly enforce HTTPS.

If no certificate and key are provided, one pair will be auto-generated. The generated certificate is only valid for localhost. It is stored in ~/.http2server. As a user convenience, the certificate is added as trusted to the operating system so browsers will accept the certificate. A password dialog may appear to confirm. This is currently only supported on macOS and Linux.

In production use Let's Encrypt or any other trusted, signed certificate.

Intermediate certificates are stapled to the OCSP response to speed up the TLS handshake.

See Also

  • http2.live β€” Static site hosting and CDN based on this server.
  • Unbundle β€” Build tool for front-end JavaScript applications, designed for HTTP/2 Server Push and Cache Digests.


Made with ❀️ by Sebastiaan Deckers in πŸ‡ΈπŸ‡¬ Singapore.

If you find any bugs or have a feature request, please open an issue on github!

The npm package download data comes from npm's download counts api and package details come from npms.io.