How to terminate a HTTP server in Node.js?

Gajus Kuizinas - Jan 20 '20 - - Dev Community

One of the most popular support questions I answer is a variation of "how to terminate a HTTP server" or "why does HTTP server not stop after calling server.close()". The reason for that is described in the documentation of server.close():

server.stop() stops the server from accepting new connections and keeps existing connections. This function is asynchronous, the server is finally closed when all connections are ended and the server emits a 'close' event.

i.e. when you call server.close(), server stops accepting new connections, but it keeps the existing connections open indefinitely. This can result in your server hanging indefinitely due to persistent HTTP connections or because of the ongoing requests that do not produce a response. Therefore, in order to close the server, you must track creation of all connections and terminate them yourself.

The basic principle of forcefully closing a HTTP server looks like this:

import {
  createServer,
} from 'http';

const server = createServer(() => {});

server.listen();

const sockets = new Set();

server.on('connection', (socket) => {
  sockets.add(socket);

  server.once('close', () => {
    sockets.delete(socket);
  });
});

/**
 * Forcefully terminates HTTP server.
 */
const close = (callback) => {
  for (const socket of sockets) {
    socket.destroy();

    sockets.delete(socket);
  }

  server.close(callback);
};

However, abruptly terminating a HTTP server is not desirable. You want to give time for existing requests to complete (without accepting new requests) and you want to inform the client that it should not attempt to use the same connection again (in case of persist HTTP connections). All of this can be done using an abstraction such as http-terminator.

http-terminator implements the logic for tracking all connections and their termination upon a timeout. http-terminator also ensures graceful communication of the server intention to shutdown to any clients that are currently receiving response from this server.

Terminating a server using http-terminator is as simple as instantiating the service referencing the HTTP server instance and calling terminate method:

import http from 'http';
import {
  createHttpTerminator,
} from 'http-terminator';

const server = http.createServer();

const httpTerminator = createHttpTerminator({
  server,
});

await httpTerminator.terminate();

By default, http-terminator gives 1 second for the server to complete the request. This can be configured using httpResponseTimeout configuration.

Finally, it is worth mentioning that there are several alternatives that implement comparable functionality, e.g.

The main benefit of http-terminator is that:

  • it does not monkey-patch Node.js API
  • it immediately destroys all sockets without an attached HTTP request
  • it allows graceful timeout to sockets with ongoing HTTP requests
  • it properly handles HTTPS connections
  • it informs connections using keep-alive that server is shutting down by setting a connection: close header
  • it does not terminate the Node.js process
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .