En este artículo te explico cómo pasar de una configuración web clásica de Apache en Ubuntu a otra en la que tenemos dos dominios alojados en la misma máquina: uno con Node.js y otro sobre Apache. Para ello usaremos nginx como proxy inverso.
La siguiente imagen muestra la arquitectura de nuestro servidor después del cambio.
1. Cambiar los puertos a Apache
Con la nueva configuración, será nginx quien atienda la conexiones del exterior por los puertos 80 (HTTP) y 443 (HTTPS). Esto quiere decir que, antes de instalarlo, debemos modificar la configuración de Apache para que «escuche» por otros puertos no estándar. De lo contrario, al instalar nginx se produciría un conflicto al intentar escuchar ambos por el mismo puerto.
Para esto, primero debemos localizar la directiva Listen 80
, probablemente en /etc/apache2/ports.conf
, y cambiarla a un puerto no estándar, digamos el 8000:
Listen 8000
Si hay alguna otra Listen
para el puerto SSL 443, la borramos o la comentamos (será nginx quien se encargue de las conexiones seguras, ya que ahora las conexiones con Apache quedan «en casa»).
Si teníamos una sentencia del tipo <VirtualHost *:80>
en la configuración del servidor, en algún fichero del estilo /etc/apache2/sites-enabled/example_com.conf
, la cambiamos también al nuevo puerto:
<VirtualHost *:8000>
Hechos estos cambios, ya podemos reiniciar el servidor para que surtan efecto:
sudo service apache2 restart
2. Instalar nginx
Hay básicamente dos maneras de instalar nginx: precompilado desde el repositorio oficial (vía apt
) y manualmente (compilando los fuentes). En este caso nos es más que suficiente la instalación de la versión estable desde el repo oficial, para lo cual seguiremos las instrucciones de instalación para Ubuntu. En resumen:
sudo apt install curl gnupg2 ca-certificates lsb-release
echo "deb http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" | sudo tee /etc/apt/sources.list.d/nginx.list
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
sudo apt update
sudo apt install nginx
Para saber si nginx se está ejecutando:
sudo service nginx status
O bien:
ps -aux | grep nginx
3. Configurar nginx como proxy inverso
La configuración del sitio principal la tenemos normalmente en /etc/nginx/conf.d/default.conf
. Ahí incluiremos la redirección permanente (301) de HTTP a HTTPS. Algo así:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name example.com www.example.com;
return 301 https://example.com$request_uri; # Redireccionar todas las peticiones
}
La configuración de la web segura la podríamos tener en un fichero aparte. Ahí es donde configuraremos nginx como proxy inverso para Apache:
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name example.com www.example.com;
ssl_certificate /ruta/de/la/cadena-de-certificados;
ssl_certificate_key /ruta/de/la/clave-privada;
# … resto de la configuración de SSL…
location / {
# Pasa la petición a Apache
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}
Con esto, nuestra web volvería a estar accesible desde el exterior tras reiniciar el servidor:
sudo nginx -s reload
Nota: ahora que estamos tras un proxy inverso, posiblemente sea necesario tocar el código del servidor (PHP o lo que sea) para tener en cuenta las cabeceras X-Forwarded-*
(para detectar correctamente si la conexión es o no segura —X-Forwarded-Proto
— o la verdadera dirección IP del cliente —X-Forwarded-For
—, por ejemplo).
4. Instalar Node.js y dependencias
Si aún no tenemos Node.js instalado, seguimos las instrucciones de instalación para Ubuntu. En estos momentos (abril 2019), la última versión con respaldo a largo plazo (LTS) es la 10.x:
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo apt-get install -y nodejs
Comprobamos que se ha instalado correctamente, preguntando por la versión:
node --version
En este punto, también deberíamos instalar cualquier otra dependencia que tenga nuestro proyecto Node.js, por ejemplo MongoDB, paquetes npm, etc.
5. Desplegar la aplicación Node.js
En cuanto al código de la aplicación, debemos tener en cuenta dos cosas:
- Configurarla para que no interprete la dirección IP del proxy como la del cliente final. En su lugar, debería mirar la cabecera HTTP X-Forwarded-For (que estableceremos en la configuración de nginx en el siguiente paso). En Express, hacemos esto con la variable de aplicación
trust proxy
. - Por supuesto, hay que programar el servidor para que atienda las peticiones por otro puerto no estándar, digamos el 9000.
Quedaría algo como lo que sigue:
const express = require('express');
const app = express();
const port = 9000;
app.set('trust proxy', true);
//...
app.listen(port, () => console.log(`Servidor activo en el puerto ${port}.`));
Una vez tenemos nuestra aplicación instalada y lista para funcionar, es hora de ejecutarla. Eso sí, lo ideal es registrarla como servicio, de manera que arranque automáticamente si se reinicia el sistema operativo. En Ubuntu, esto es tan sencillo como crear un fichero, p. ej. nodeapp.service
, en la ruta /etc/systemd/system
. Por ejemplo:
[Unit]
Description=Mi fantabulosa web example.net
# Si la aplicación se conecta a una base de datos MongoDB…
Requires=network.target mongod.service
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/node /home/perez/www/example_net/index.js
WorkingDirectory=/home/perez/www/example_net
# Si la aplicación se aborta por algún motivo, reiniciamos a los tres segundos
Restart=always
RestartSec=3
StandardInput=null
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=example_net-webapp
# Variables de entorno que necesite nuestra aplicación
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target
Con esto, ya podemos iniciar la aplicación como servicio:
sudo systemctl start nodeapp
6. Configurar la nueva web en nginx
Ya solo queda configurar nginx para recibir las peticiones a nuestro nuevo dominio, example.net, y actuar como proxy inverso de la web Node.js. Añadiríamos un nuevo fichero, por ejemplo example_net.conf
, al directorio de configuración /etc/nginx/conf.d
:
server {
listen 80;
listen [::]:80;
server_name example.net www.example.net;
return 301 https://example.net$request_uri; # Redirección HTTP → HTTPS
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.net www.example.net;
# Configuración SSL…
# ...
location / {
# Pasamos la petición a la aplicación Node.js
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}
Aplicamos los cambios…
sudo nginx -s reload
Listo
…y listo. Tenemos funcionando, si los dioses nos son propicios, example.com (Apache) y example.net (Node.js) en el mismo servidor (y más que podríamos fácilmente añadir en el futuro). Además, tenemos un flamante nginx que podremos tunear a nuestro antojo para mejorar el rendimiento del servidor (cachés, limitación de peticiones, servir ficheros estáticos, redirecciones, seguridad y un largo etcétera).
Pero ya iremos viendo algunas de esas técnicas en próximos artículos. Ahora lo que toca es un merecido descanso.