Recommendations

This document contains a set recommendations, or best practices, when using Fastify.

Use A Reverse Proxy

Node.js is an early adopter of frameworks shipping with an easy to use web server within the standard library. Previously, with languages like PHP or Python, one would need either a web server with specific support for the language or the ability to setup some sort of CGI gateway that works with the language. With Node.js, one can simply write an application that directly handles HTTP requests. As a result, the temptation is to write applications that handle requests for multiple domains, listen on multiple ports (i.e. HTTP and HTTPS), and various other scenarios and combinations thereof. Further, the temptation is to then expose these applications directly to the Internet to handle requests.

The Fastify team strongly considers this to be an anti-pattern and extremely bad practice:

For a concrete example, consider the situation where:

  • The app needs multiple instances to handle load.
  • The app needs TLS termination.
  • The app needs to redirect HTTP requests to HTTPS.
  • The app needs to serve multiple domains.
  • The app needs to serve static resources, e.g. jpeg files.There are many reverse proxy solutions available, and your environment may dictate the solution to use, e.g. AWS or GCP. But given the above, we could use HAProxy to solve these requirements:
  1. # The global section defines base HAProxy (engine) instance configuration.
  2. global
  3. log /dev/log syslog
  4. maxconn 4096
  5. chroot /var/lib/haproxy
  6. user haproxy
  7. group haproxy
  8. # Set some baseline TLS options.
  9. tune.ssl.default-dh-param 2048
  10. ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
  11. ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
  12. ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11
  13. ssl-default-server-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
  14. # Each defaults section defines options that will apply to each subsequent
  15. # subsection until another defaults section is encountered.
  16. defaults
  17. log global
  18. mode http
  19. option httplog
  20. option dontlognull
  21. retries 3
  22. option redispatch
  23. maxconn 2000
  24. timeout connect 5000
  25. timeout client 50000
  26. timeout server 50000
  27. # Enable content compression for specific content types.
  28. compression algo gzip
  29. compression type text/html text/plain text/css application/javascript
  30. # A "frontend" section defines a public listener, i.e. an "http server"
  31. # as far as clients are concerned.
  32. frontend proxy
  33. # The IP address here would be the _public_ IP address of the server.
  34. # Here, we use a private address as an example.
  35. bind 10.0.0.10:80
  36. # This redirect rule will redirect all traffic that is not TLS traffic
  37. # to the same incoming request URL on the HTTPS port.
  38. redirect scheme https code 308 if !{ ssl_fc }
  39. # Technically this use_backend directive is useless since we are simply
  40. # redirecting all traffic to this frontend to the HTTPS frontend. It is
  41. # merely included here for completeness sake.
  42. use_backend default-server
  43. # This frontend defines our primary, TLS only, listener. It is here where
  44. # we will define the TLS certificates to expose and how to direct incoming
  45. # requests.
  46. frontend proxy-ssl
  47. # The `/etc/haproxy/certs` directory in this example contains a set of
  48. # certificate PEM files that are named for the domains the certificates are
  49. # issued for. When HAProxy starts, it will read this directory, load all of
  50. # the certificates it finds here, and use SNI matching to apply the correct
  51. # certificate to the connection.
  52. bind 10.0.0.10:443 ssl crt /etc/haproxy/certs
  53. # Here we define rule pairs to handle static resources. Any incoming request
  54. # that has a path starting with `/static`, e.g.
  55. # `https://one.example.com/static/foo.jpeg`, will be redirected to the
  56. # static resources server.
  57. acl is_static path -i -m beg /static
  58. use_backend static-backend if is_static
  59. # Here we define rule pairs to direct requests to appropriate Node.js
  60. # servers based on the requested domain. The `acl` line is used to match
  61. # the incoming hostname and define a boolean indicating if it is a match.
  62. # The `use_backend` line is used to direct the traffic if the boolean is
  63. # true.
  64. acl example1 hdr_sub(Host) one.example.com
  65. use_backend example1-backend if example1
  66. acl example2 hdr_sub(Host) two.example.com
  67. use_backend example2-backend if example2
  68. # Finally, we have a fallback redirect if none of the requested hosts
  69. # match the above rules.
  70. default_backend default-server
  71. # A "backend" is used to tell HAProxy where to request information for the
  72. # proxied request. These sections are where we will define where our Node.js
  73. # apps live and any other servers for things like static assets.
  74. backend default-server
  75. # In this example we are defaulting unmatched domain requests to a single
  76. # backend server for all requests. Notice that the backend server does not
  77. # have to be serving TLS requests. This is called "TLS termination": the TLS
  78. # connection is "terminated" at the reverse proxy.
  79. # It is possible to also proxy to backend servers that are themselves serving
  80. # requests over TLS, but that is outside the scope of this example.
  81. server server1 10.10.10.2:80
  82. # This backend configuration will serve requests for `https://one.example.com`
  83. # by proxying requests to three backend servers in a round-robin manner.
  84. backend example1-backend
  85. server example1-1 10.10.11.2:80
  86. server example1-2 10.10.11.2:80
  87. server example2-2 10.10.11.3:80
  88. # This one serves requests for `https://two.example.com`
  89. backend example2-backend
  90. server example2-1 10.10.12.2:80
  91. server example2-2 10.10.12.2:80
  92. server example2-3 10.10.12.3:80
  93. # This backend handles the static resources requests.
  94. backend static-backend
  95. server static-server1 10.10.9.2:80