En ocasiones tenemos servicios o aplicaciones web que escuchan directamente a un puerto interno. Para evitar acceder a dicha aplicación o servicio por la IP del servidor, una configuración que podemos hacer es acceder desde un Nginx Proxy.


En ocasiones tenemos servicios o aplicaciones web que escuchan directamente en un puerto interno. Para evitar acceder a dichas aplicaciones por la IP del servidor o por puertos no estándar, podemos utilizar un proxy inverso con Nginx.

Esta configuración nos ofrece varias ventajas, como poder acceder mediante un dominio o subdominio y habilitar un certificado SSL fácilmente con Let's Encrypt.

En este tutorial explicaremos cómo configurar Nginx para que actúe como proxy hacia un puerto interno, de modo que sea Nginx quien gestione las peticiones externas, incluyendo HTTPS. Partimos de la base de que Nginx ya está instalado y en funcionamiento en el servidor.

Requisitos previos

Antes de comenzar, asegúrate de tener instalados los siguientes paquetes:

apt install nginx certbot python3-certbot-nginx

Además, el dominio o subdominio que vayas a usar debe apuntar correctamente a la IP pública del servidor.

Configuración de Nginx Proxy

Tendremos que crear o editar nuestro fichero de configuración para Nginx de nuestra aplicación, por lo general se debería de encontrar en /etc/nginx/sites-enabled y depende de si tenemos certificado SSL o no se deberá de hacer de una manera u otra.

Generar certificado SSL con Certbot

Si todavía no tienes certificado SSL, lo más recomendable es generarlo con el método webroot de Certbot. Este método permite validar el dominio sin necesidad de detener Nginx, ya que la validación se realiza a través de una ruta temporal servida por el propio Nginx.

Primero, creamos un pequeño bloque de servidor para la validación y redirección HTTP a HTTPS. Este bloque debe estar en /etc/nginx/sites-enabled y puede tener el siguiente contenido:

server {
  listen 80;
  server_name [SUB_DOMINIO];
  
  location /.well-known/acme-challenge/ {
    root /var/www/letsencrypt;
  }

  location / {
    return 301 https://$host$request_uri;
  }
}
💡
Recuerda de modificar [DOMINIO] por el dominio o subdominio que necesites configurar y que esté apuntando a la IP del servidor.

Recargamos el servicio de Nginx con la nueva configuración:

systemctl reload nginx

A continuación, creamos el directorio para los archivos temporales de validación y generamos el certificado:

mkdir -p /var/www/letsencrypt
certbot certonly --webroot -w /var/www/letsencrypt -d [DOMINIO]
💡
Recuerda de modificar [SUB_DOMINIO] por el dominio o subdominio que necesites configurar y que esté apuntando a la IP del servidor.

Si la validación es correcta, se generará el certificado en:

  • /etc/letsencrypt/live/[DOMINIO]/fullchain.pem
  • /etc/letsencrypt/live/[DOMINIO]/privkey.pem

Certbot también configura una tarea automática para renovar el certificado periódicamente. Puedes probar la renovación en cualquier momento con:

certbot renew --dry-run

Configuración del Proxy Nginx con el SSL generado

Una vez generado el certificado, creamos el bloque de servidor que actuará como proxy inverso. Este bloque gestionará el tráfico HTTPS, reenviando las peticiones al puerto interno donde escucha nuestra aplicación o servicio.

server {
  listen 80;
  server_name [SUB_DOMINIO];
  
  location /.well-known/acme-challenge/ {
    root /var/www/letsencrypt;
  }

  location / {
    return 301 https://$host$request_uri;
  }
}

server {
  listen 443 ssl http2;
  server_name [SUB_DOMINIO];
  
  ssl_certificate /etc/letsencrypt/live/[SUB_DOMINIO]/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/[SUB_DOMINIO]/privkey.pem;
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers on;
  
  location / {
      proxy_pass http://localhost:[PUERTO_INTERNO];
      proxy_http_version 1.1;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $remote_addr;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_pass_header Server;
      proxy_redirect off;
      proxy_set_header Connection "";
  }
  
  add_header X-Frame-Options "SAMEORIGIN";
  add_header X-Content-Type-Options "nosniff";
  add_header Referrer-Policy "strict-origin-when-cross-origin";
  add_header Content-Security-Policy "upgrade-insecure-requests";
  
  access_log /var/log/nginx/[SUB_DOMINIO]_access.log;
  error_log /var/log/nginx/[SUB_DOMINIO]_error.log warn;
}
💡
Deberás reemplazar los valores [SUB_DOMINIO] y [PUERTO_INTERNO] por los correspondientes a tu aplicación.

Ejemplo práctico con una APP

Por ejemplo, si tenemos una aplicación ejecutándose en el puerto interno 3000, la configuración sería:

server {
  listen 80;
  server_name miapp.voidnull.es;
  
  location /.well-known/acme-challenge/ {
    root /var/www/letsencrypt;
  }

  location / {
    return 301 https://$host$request_uri;
  }
}

server {
  listen 443 ssl http2;
  server_name miapp.voidnull.es;
  
  ssl_certificate /etc/letsencrypt/live/miapp.voidnull.es/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/miapp.voidnull.es/privkey.pem;
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers on;
  
  location / {
      proxy_pass http://localhost:3000;
      proxy_http_version 1.1;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $remote_addr;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_pass_header Server;
      proxy_redirect off;
      proxy_set_header Connection "";
  }
  
  add_header X-Frame-Options "SAMEORIGIN";
  add_header X-Content-Type-Options "nosniff";
  add_header Referrer-Policy "strict-origin-when-cross-origin";
  add_header Content-Security-Policy "upgrade-insecure-requests";
  
  access_log /var/log/nginx/miapp.voidnull.es_access.log;
  error_log /var/log/nginx/miapp.voidnull.es_error.log warn;
}

Verificación y reinicio del servicio

Antes de reiniciar Nginx, es importante comprobar que la configuración es válida:

nginx -t

Si no hay errores, recarga el servicio para aplicar los cambios:

systemctl reload nginx

Renovación automática

Certbot crea una tarea automática mediante systemd o cron para renovar los certificados antes de su expiración. Puedes verificar el estado o forzar una prueba de renovación con:

certbot renew --dry-run

Con la configuración anterior (usando el método webroot), la renovación se realizará sin necesidad de detener Nginx ni modificar la configuración.

Conclusión

Con esta configuración, Nginx actuará como proxy inverso hacia cualquier servicio o aplicación que escuche en un puerto interno, ofreciendo acceso mediante HTTPS con Let’s Encrypt y cabeceras de seguridad adicionales.

Es una forma sencilla, segura y estandarizada de exponer aplicaciones internas con certificados válidos y redirección automática desde HTTP a HTTPS.


Más sobre ./voidNull

Haz que cada palabra cuente: tu donación nos inspira a seguir creando contenido. Accede al apartado de Donación para hacer tu aportación