Servidor nginx con NodeJS

Montar un proxy inverso con nginx + pm2


En este artículo vamos a explicar los pasos necesarios para montar y configurar un servidor de NodeJS, mediante un servidor web Nginx como proxy inverso y PM2 como gestor de proceso de NodeJS.

¿Qué es un proxy inverso?

Un proxy inverso es un programa (servidor web) que se encarga de redirigir las peticiones entrantes a otros servidores. Esto nos proporciona la ventaja de utilizar nginx como capa inicial, que se encargará de gestionar las peticiones sencillas de archivos estáticos (html, imágenes, etc...) y las peticiones más complejas, pasárselas a NodeJS para que las procese ahí.

Proxy inverso con Nginx y PM2

Esto permite que nuestro servidor sea más rápido y eficiente, aproveche mejor los recursos, así como beneficiarnos de todas las ventanas profesionales que nos brinda nginx de seguridad, rendimiento, cache y otros.

Instalación de nginx

Para instalar nginx, basta con ejecutar el comando apt install nginx en nuestra terminal. Esto instalará la última versión disponible del servidor web nginx:

$ sudo apt install nginx

$ nginx -version
nginx version: nginx/1.22.1

Podemos ejecutar nginx -version después de la instalación para comprobar la versión instalada y si realmente se ha instalado correctamente.

Configuración de nginx

Por defecto, nginx tiene una página por defecto configurada en el archivo /etc/nginx/sites-enabled/default. Vamos a modificarla para configurar nuestro sitio en ella. Abrimos el fichero de configuración con un editor de textos:

$ sudo vim /etc/nginx/sites-enabled/default

En este fichero de configuración debemos crear las siguientes lineas para configurarlo correctamente. Vamos a explicarlo por partes. Localiza las primeras secciones que explicaremos en breve:

  • 1️⃣ El bloque server configura el servidor general.
  • 2️⃣ Dentro, el bloque location / configura el acceso a archivos estáticos.
  • 3️⃣ Luego, el bloque location @backend configura el acceso a archivos de NodeJS.
  • 4️⃣ Por último, el bloque upstream backend que aparece al principio, define nuestro servidor NodeJS.

El fichero de configuración de nginx quedaría así:

upstream backend {
  server localhost:3001;
}

server {
  listen 80;
  server_name _;

  # Servidor de estáticos
  location / {
    root /var/www/domain.com/public/;
    try_files $uri $uri/ @backend;
  }

  # Servidor de NodeJS
  location @backend {
    proxy_pass http://backend;

    # Reescritura de encabezados originales
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $http_host;

    # Compatibilidad con websockets
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_cache_bypass $http_upgrade;
  }
}

Expliquemos cada uno de los bloques mencionados.

El bloque server

El bloque server configura el servidor general. En este caso, lo único que hace es indicarle a Nginx que escuche en el puerto 80 (http). Además, con server_name _ se indica que el servidor es genérico, y que accederemos a él desde cualquier dominio.

El bloque location /

Este bloque se utiliza para definir la ruta de acceso a los ficheros estáticos. Con root definimos la ruta donde tenemos nuestros archivos. Observa que se trata de la ruta /var/www/domain.com/public/. Nuestra ruta /var/www/domain.com es la carpeta donde tenemos los archivos de nuestra aplicación, mientras que en la subcarpeta public/ tenemos todos los archivos estáticos de front-end que se sirven directamente y no necesitan un servidor como NodeJS.

Por otro lado, el try_files nos indica que si existe un archivo $uri o una carpeta $uri/ entonces la procese con nginx, pero en caso contrario, se dirija al bloque @backend.

El bloque location @backend

Este bloque se utiliza para definir que debe hacer nginx cuando le llegue una potencial petición para NodeJS. Rutas como por ejemplo /, /api/, /faq/ o /about/ son rutas que, si no existen dichas carpetas, serán procesadas por Node, donde si tenemos un servidor de endpoints con express, las procesará ahí. Si acaso existieran, serían procesadas con el try_files de Nginx que mencionamos antes, en caso contrario con proxy_pass lo enviamos al upstream que explicaremos más adelante.

Observa que tenemos varias directivas proxy_set_header o similares, que sirven para reescribir cabeceras o añadir compatibilidad que se puede perder al redireccionar a otro lugar.

El bloque upstream

Por último, el bloque upstream nos indica que tenemos un servidor escuchando en esta máquina localhost y en el puerto 3001. Ahí debemos poner a escuchar PM2, con nuestro script de NodeJS.

Arrancar los servidores

Una vez tenemos guardada esta configuración, vamos a comprobar si todo está escrito correctamente. Ejecutamos el comando nginx -t y si todo ha ido bien, nos debería salir algo como esto:

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

$ sudo service nginx restart
Restarting nginx: nginx.

Una vez comprobado, escribimos sudo service nginx restart y reiniciamos el servicio para que tome los cambios de configuración. Con esto tendríamos nginx funcionando.

Ahora, vamos a arrancar pm2 con el script de NodeJS, que presumiblemente tendríamos en /var/www/domain.com. Si no conoces pm2, lo explicamos previamente en el artículo PM2: Gestor de procesos:

$ pm2 start /var/www/domain.com/index.js --name app

$ pm2 save

Recapitulando, tendríamos 2 servicios funcionando:

  • Nginx en el puerto 80, vigilando todas las peticiones entrantes
  • PM2 en el puerto 3001, esperando las peticiones redirigidas por nginx
  • Si realizamos peticiones como /index.html o /imagen.jpg, si existen, las devolvería nginx.
  • Si realizamos peticiones como /about o /api/users, si existen, las procesaría NodejS vía PM2.

Futuros pasos

Recuerda que esto es una versión simplificada para entender como montar un servidor nginx y pm2. Hay ciertas características que sería ideal tener también en cuenta:

  • Establecer variables de entorno adecuadas en PM2
  • Configurar Nginx para HTTPS (SSL) en lugar de HTTP
  • Establecer un firewall para limitar el acceso al puerto 3001 desde fuera del servidor
  • Añadir la gestión de 404 en los endpoints de NodeJS
  • Considerar utilizar Docker para tener todo bien aislado y organizado
  • Establecer un workflow de despliegue automatizado

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev