Cómo instalar y configurar Nginx en Debian 13: guía completa de Server Blocks, PHP, HTTPS y producción

Cuando queremos publicar una página web en Internet necesitamos un servidor capaz de recibir solicitudes HTTP y entregar los archivos o respuestas correspondientes.
Una de las herramientas más utilizadas para esta tarea es Nginx, un servidor web que también puede funcionar como proxy inverso, balanceador de carga y servidor de contenido estático.
Nginx es una alternativa a Apache HTTP Server y se utiliza para alojar páginas HTML, CSS y JavaScript, ejecutar aplicaciones PHP mediante PHP-FPM y publicar sitios con dominios reales y certificados HTTPS.
En este tutorial aprenderemos a instalar y configurar Nginx en Debian 13 desde cero.
Comenzaremos con un laboratorio local utilizando /etc/hosts y posteriormente prepararemos un sitio para publicarlo en un VPS.
También aprenderemos a interpretar sus archivos de configuración, administrar varios dominios y aplicar medidas básicas de seguridad.
1. ¿Qué es Nginx?
Nginx, pronunciado habitualmente engine-x, es un servidor web diseñado para procesar conexiones y solicitudes HTTP mediante una arquitectura orientada a eventos.
Además de entregar archivos estáticos, puede realizar funciones como:
- Servir páginas HTML, CSS, JavaScript e imágenes.
- Comunicarse con PHP-FPM para ejecutar aplicaciones PHP.
- Actuar como proxy inverso para aplicaciones.
- Distribuir solicitudes entre varios servidores.
- Gestionar conexiones HTTPS.
- Aplicar reglas de redirección y reescritura.
- Administrar múltiples sitios web mediante bloques
server.
Su arquitectura permite manejar numerosas conexiones con un número relativamente reducido de procesos, aunque el rendimiento real depende de la configuración, los recursos y las aplicaciones.
¿Cómo funciona?
Cuando un navegador solicita una página:
http://empresa.test
Nginx recibe la solicitud, determina qué bloque server corresponde al nombre solicitado y procesa las directivas de configuración.
Si el recurso es un archivo HTML, CSS o JavaScript, puede entregarlo directamente.
Si se solicita un archivo PHP, normalmente lo envía a PHP-FPM para que sea interpretado.
NAVEGADOR
|
v
NGINX
|
+---- HTML / CSS / JavaScript
| |
| v
| NAVEGADOR
|
+---- PHP
|
v
PHP-FPM
|
v
RESPUESTA GENERADA
|
v
NAVEGADOR
Importante: Nginx no interpreta PHP por sí mismo. Necesita comunicarse con un procesador compatible, como PHP-FPM.
2. ¿En qué sistemas operativos podemos utilizar Nginx?
Nginx está disponible para diferentes sistemas operativos.
| Sistema | Paquete habitual | Gestor |
|---|---|---|
| Debian 13 | nginx | APT |
| Debian 12 | nginx | APT |
| Ubuntu Server | nginx | APT |
| Linux Mint | nginx | APT |
| Fedora | nginx | DNF |
| Rocky Linux | nginx | DNF |
| AlmaLinux | nginx | DNF |
| Arch Linux | nginx | Pacman |
También existen distribuciones y entornos donde Nginx puede ejecutarse mediante contenedores.
En este artículo utilizaremos Debian 13.
La mayoría de los procedimientos también pueden aplicarse a Ubuntu Server, aunque es necesario comprobar las versiones y rutas de configuración.
3. Diferencias principales entre Apache2 y Nginx
Ambos pueden alojar páginas web y ejecutar aplicaciones PHP mediante PHP-FPM.
Sin embargo, existen diferencias en su configuración.
| Característica | Apache2 | Nginx |
|---|---|---|
| Configuración de sitios | <VirtualHost> | server {} |
| Directorio público | DocumentRoot | root |
| Nombre del dominio | ServerName | server_name |
| Configuración por directorio | <Directory> | location y otras directivas |
.htaccess | Compatible | No compatible |
| PHP | mod_php o PHP-FPM | Habitualmente PHP-FPM |
| Validación | apache2ctl configtest | nginx -t |
| Recarga | systemctl reload apache2 | systemctl reload nginx |
Nginx no es automáticamente mejor que Apache en todos los escenarios.
La elección depende del proyecto, los requisitos de la aplicación, el tráfico y los conocimientos de administración.
4. Instalar Nginx en Debian 13
Actualizamos la información de los paquetes:
sudo apt update
Instalamos Nginx:
sudo apt install nginx
Comprobamos el servicio:
systemctl status nginx
Si funciona correctamente, veremos un estado similar a:
Active: active (running)
Iniciar Nginx
sudo systemctl start nginx
Detener Nginx
sudo systemctl stop nginx
Reiniciar Nginx
sudo systemctl restart nginx
Recargar la configuración
sudo systemctl reload nginx
Habilitar el inicio automático
sudo systemctl enable nginx
También podemos iniciar y habilitar el servicio simultáneamente:
sudo systemctl enable --now nginx
¿Qué pasa si Apache2 ya está instalado?
Apache y Nginx pueden entrar en conflicto si ambos intentan escuchar en las mismas direcciones y puertos, especialmente 80 y 443.
Podemos comprobar qué proceso utiliza el puerto 80:
sudo ss -ltnp '( sport = :80 )'
Si estamos realizando un laboratorio exclusivamente con Nginx y Apache no es necesario, podemos detenerlo:
sudo systemctl stop apache2
No debemos deshabilitar Apache en un servidor que ya aloja aplicaciones activas sin planificar su migración.
Ambos servidores también pueden trabajar juntos si configuramos puertos y funciones diferentes.
5. Comprobar que Nginx funciona
Abrimos nuestro navegador y visitamos:
http://localhost
También podemos utilizar:
http://127.0.0.1
Si Nginx está funcionando y conserva su sitio predeterminado, veremos la página inicial correspondiente.
Desde la terminal:
curl -I http://localhost
Una respuesta habitual será:
HTTP/1.1 200 OK
El código 200 indica que la solicitud se procesó correctamente.
Si aparece un error, revisamos:
sudo systemctl status nginx
Y:
sudo journalctl -u nginx --since "10 minutes ago"
6. Conocer los archivos de configuración de Nginx
En Debian, la configuración principal se encuentra en:
/etc/nginx/
Una estructura habitual es:
/etc/nginx/
├── nginx.conf
├── conf.d/
├── sites-available/
├── sites-enabled/
├── snippets/
├── mime.types
├── fastcgi_params
└── fastcgi.conf
nginx.conf
Es el archivo principal de configuración.
Contiene directivas generales, como la cantidad de procesos trabajadores y la inclusión de otras configuraciones.
sites-available
Almacena archivos de configuración de sitios web.
Por ejemplo:
/etc/nginx/sites-available/empresa.test
/etc/nginx/sites-available/tienda.test
sites-enabled
Contiene normalmente enlaces simbólicos a los sitios habilitados.
A diferencia de Apache, Nginx no utiliza de forma estándar los comandos a2ensite y a2dissite.
En Debian podemos crear los enlaces simbólicos manualmente.
conf.d
Permite incluir archivos de configuración adicionales.
snippets
Almacena fragmentos reutilizables de configuración.
Por ejemplo, configuraciones relacionadas con PHP o certificados.
mime.types
Contiene asociaciones entre extensiones de archivos y tipos MIME.
Esto ayuda a Nginx a indicar correctamente el tipo de contenido que entrega.
7. Comprender la sintaxis de Nginx
Antes de crear nuestro primer sitio debemos comprender cómo se organiza su configuración.
Un ejemplo sencillo:
server {
listen 80;
server_name empresa.test;
root /var/www/empresa.test/public;
index index.html;
}
¿Qué significa server?
El bloque:
server {
}
Define un servidor virtual.
Permite establecer reglas para un nombre de dominio, puerto o conjunto de solicitudes.
¿Qué significa listen?
listen 80;
Indica que este bloque recibe solicitudes HTTP en el puerto 80.
¿Qué significa server_name?
server_name empresa.test;
Define el nombre de dominio asociado al bloque.
¿Qué significa root?
root /var/www/empresa.test/public;
Indica el directorio desde el cual Nginx buscará los archivos públicos.
¿Qué significa index?
index index.html;
Establece qué archivo se utilizará como documento índice cuando corresponda.
¿Por qué las líneas terminan en punto y coma?
En la sintaxis de Nginx, muchas directivas terminan con ;.
Los bloques utilizan llaves:
{
}
Si olvidamos un punto y coma o una llave, Nginx puede rechazar la configuración.
Por eso debemos validar los cambios antes de recargar el servicio.
8. ¿Qué es un Server Block?
En Nginx, los bloques server cumplen una función comparable a los VirtualHosts de Apache.
Nos permiten alojar varias páginas web en una misma máquina.
Por ejemplo:
SERVIDOR DEBIAN
IP: 192.168.1.100
|
v
NGINX
|
+--- empresa.test
|
+--- tienda.test
|
+--- academia.test
Cada sitio puede tener su propio directorio, configuración y registros.
El nombre Server Block se utiliza habitualmente para describir esta configuración.
9. Laboratorio: crear nuestro primer sitio local
Vamos a crear:
empresa.test
Utilizaremos .test porque es un dominio reservado para pruebas.
Paso 1. Crear el directorio
sudo mkdir -p /var/www/empresa.test/public
Paso 2. Crear index.html
sudo nano /var/www/empresa.test/public/index.html
Contenido:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Empresa - Nginx</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<main>
<h1>¡Nginx funciona correctamente!</h1>
<p>
Mi primera página web con Nginx
en Debian 13.
</p>
<button id="boton">
Probar JavaScript
</button>
</main>
<script src="js/script.js"></script>
</body>
</html>
Paso 3. Crear CSS y JavaScript
sudo mkdir -p /var/www/empresa.test/public/css
sudo mkdir -p /var/www/empresa.test/public/js
Creamos CSS:
sudo nano /var/www/empresa.test/public/css/styles.css
Contenido:
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
background: #0d1117;
color: #e6edf3;
font-family: Arial, sans-serif;
}
main {
width: min(90%, 600px);
padding: 40px;
text-align: center;
background: #161b22;
border: 1px solid #00ff41;
border-radius: 16px;
}
h1 {
color: #00ff41;
}
button {
padding: 12px 24px;
background: #00ff41;
color: #0d1117;
border: none;
border-radius: 8px;
cursor: pointer;
}
Ahora JavaScript:
sudo nano /var/www/empresa.test/public/js/script.js
Contenido:
"use strict";
const boton = document.querySelector("#boton");
boton.addEventListener("click", () => {
alert("JavaScript funciona correctamente con Nginx.");
});
Con esto tenemos una página estática que utiliza HTML, CSS y JavaScript.
10. Crear el primer Server Block
Creamos:
sudo nano /etc/nginx/sites-available/empresa.test
Contenido:
server {
listen 80;
server_name empresa.test;
root /var/www/empresa.test/public;
index index.html;
location / {
try_files $uri $uri/ =404;
}
access_log /var/log/nginx/empresa.test-access.log;
error_log /var/log/nginx/empresa.test-error.log;
}
Vamos a explicar las nuevas directivas.
location /
location / {
}
Define cómo procesar las solicitudes que coinciden con esa ubicación.
En este caso, es una configuración general para las rutas del sitio.
try_files
try_files $uri $uri/ =404;
Indica a Nginx que intente encontrar el recurso solicitado.
Primero comprueba la ruta del archivo.
Después comprueba si corresponde a un directorio.
Si no encuentra un recurso válido, devuelve un error 404.
access_log
access_log /var/log/nginx/empresa.test-access.log;
Define dónde se registrarán las solicitudes.
error_log
error_log /var/log/nginx/empresa.test-error.log;
Define el archivo de errores del sitio.
11. Habilitar el sitio web
En Debian podemos habilitar el sitio creando un enlace simbólico:
sudo ln -s \
/etc/nginx/sites-available/empresa.test \
/etc/nginx/sites-enabled/empresa.test
¿Qué hace ln -s?
ln permite crear enlaces.
La opción -s indica que será un enlace simbólico.
En lugar de duplicar el archivo, creamos una referencia hacia la configuración original.
Validar Nginx
sudo nginx -t
Si la configuración es correcta, veremos mensajes similares a:
syntax is ok
test is successful
Recargamos:
sudo systemctl reload nginx
Deshabilitar un sitio
Podemos retirar el enlace simbólico:
sudo rm /etc/nginx/sites-enabled/empresa.test
Esto no elimina el archivo original de sites-available.
Después debemos validar y recargar Nginx.
12. Configurar /etc/hosts para dominios locales
Hasta ahora Nginx reconoce:
empresa.test
Pero nuestro equipo todavía necesita resolver ese nombre.
Editamos:
sudo nano /etc/hosts
Agregamos:
127.0.0.1 empresa.test
Guardamos.
Ahora visitamos:
http://empresa.test
Si todo está correctamente configurado, veremos nuestra página.
¿Qué sucede si queremos acceder desde otra computadora?
Debemos utilizar la dirección IP privada del servidor.
Por ejemplo:
192.168.1.100 empresa.test
Esta entrada debe existir en el equipo cliente o resolverse mediante un DNS interno.
La dirección 127.0.0.1 siempre representa el propio dispositivo.
13. Crear una segunda página en el mismo Nginx
Ahora crearemos:
tienda.test
Primero:
sudo mkdir -p /var/www/tienda.test/public
Creamos una página:
sudo nano /var/www/tienda.test/public/index.html
Contenido:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Mi tienda</title>
</head>
<body>
<h1>Bienvenido a mi tienda</h1>
<p>Segundo sitio funcionando con Nginx.</p>
</body>
</html>
Creamos su configuración:
sudo nano /etc/nginx/sites-available/tienda.test
Contenido:
server {
listen 80;
server_name tienda.test;
root /var/www/tienda.test/public;
index index.html;
location / {
try_files $uri $uri/ =404;
}
access_log /var/log/nginx/tienda.test-access.log;
error_log /var/log/nginx/tienda.test-error.log;
}
Habilitamos:
sudo ln -s \
/etc/nginx/sites-available/tienda.test \
/etc/nginx/sites-enabled/tienda.test
Validamos:
sudo nginx -t
Recargamos:
sudo systemctl reload nginx
En /etc/hosts agregamos:
127.0.0.1 empresa.test tienda.test
Ahora tenemos dos sitios independientes:
http://empresa.test
http://tienda.test
Ambos utilizan el mismo servidor y el mismo puerto 80.
14. Probar un sitio sin modificar /etc/hosts
Podemos enviar una solicitud con un encabezado Host específico:
curl -H "Host: empresa.test" http://127.0.0.1/
Para el segundo sitio:
curl -H "Host: tienda.test" http://127.0.0.1/
Esto permite comprobar rápidamente cómo selecciona Nginx el bloque server.
También podemos consultar toda la configuración efectiva:
sudo nginx -T
Este comando valida y muestra la configuración cargada, incluidos los archivos incorporados mediante include.
Precaución: la salida puede contener rutas y valores de configuración sensibles. No debemos publicarla sin revisarla.
15. Instalar PHP-FPM en Debian 13
Hasta ahora hemos servido archivos estáticos.
Ahora habilitaremos PHP.
En Debian 13, la versión predeterminada de PHP es PHP 8.4.
Instalamos:
sudo apt install \
php-fpm \
php-cli \
php-mysql \
php-curl \
php-mbstring \
php-xml \
php-zip
No todas las aplicaciones necesitan todas las extensiones.
En producción conviene instalar únicamente las necesarias.
Comprobar PHP
php -v
Comprobar PHP-FPM
systemctl status php8.4-fpm
Habilitar el servicio
sudo systemctl enable --now php8.4-fpm
Consultar el socket
ls -l /run/php/
En una instalación habitual encontraremos:
php8.4-fpm.sock
El nombre exacto debe comprobarse en el sistema.
16. Configurar Nginx para ejecutar PHP
Editamos nuestro sitio:
sudo nano /etc/nginx/sites-available/empresa.test
Utilizaremos esta configuración:
server {
listen 80;
server_name empresa.test;
root /var/www/empresa.test/public;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri =404;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
location ~ /\.(?!well-known/) {
deny all;
}
access_log /var/log/nginx/empresa.test-access.log;
error_log /var/log/nginx/empresa.test-error.log;
}
¿Qué significa location ~ .php$?
Es un bloque de ubicación que utiliza una expresión regular.
Permite identificar solicitudes que terminan en .php.
¿Qué hace fastcgi_pass?
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
Envía la solicitud PHP al socket de PHP-FPM.
¿Qué hace include snippets/fastcgi-php.conf?
Incorpora un fragmento de configuración proporcionado habitualmente por los paquetes de Debian.
Contiene parámetros necesarios para comunicar correctamente Nginx con PHP-FPM.
Debemos comprobar que el archivo exista:
ls /etc/nginx/snippets/fastcgi-php.conf
¿Qué hace deny all?
location ~ /\.(?!well-known/) {
deny all;
}
Esta regla bloquea solicitudes hacia determinadas rutas cuyo nombre comienza con un punto, conservando una excepción para rutas que comienzan por .well-known/.
Es una protección complementaria. No sustituye una organización segura del DocumentRoot ni protege automáticamente todos los archivos confidenciales.
Validar y recargar
sudo nginx -t
sudo systemctl reload nginx
17. Crear una página PHP de prueba
Creamos:
sudo nano /var/www/empresa.test/public/prueba.php
Contenido:
<?php
declare(strict_types=1);
$fecha = date('Y-m-d H:i:s');
$version = PHP_VERSION;
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Prueba de PHP</title>
</head>
<body>
<h1>PHP funciona con Nginx</h1>
<p>
Fecha del servidor:
<?= htmlspecialchars($fecha, ENT_QUOTES, 'UTF-8') ?>
</p>
<p>
Versión de PHP:
<?= htmlspecialchars($version, ENT_QUOTES, 'UTF-8') ?>
</p>
</body>
</html>
Abrimos:
http://empresa.test/prueba.php
Si todo funciona correctamente, veremos la respuesta HTML generada por PHP.
El código PHP no debe mostrarse como texto fuente.
Si Nginx entrega el código PHP sin interpretarlo, debemos retirar temporalmente el sitio del acceso público y revisar la configuración de PHP-FPM.
18. Comprender los permisos de archivos
En Debian, los procesos trabajadores de Nginx suelen ejecutarse como:
www-data
Podemos comprobarlo:
ps aux | grep '[n]ginx'
Normalmente veremos un proceso principal y varios procesos trabajadores.
Para sitios estáticos, son habituales estos permisos:
Directorios: 755
Archivos: 644
Por ejemplo:
sudo find /var/www/empresa.test -type d -exec chmod 755 {} +
sudo find /var/www/empresa.test -type f -exec chmod 644 {} +
Si el sitio debe permanecer bajo control de root:
sudo chown -R root:root /var/www/empresa.test
Esto permite que Nginx lea los archivos públicos, pero no los modifique libremente.
¿Debemos utilizar chmod 777?
No como solución general.
El comando:
sudo chmod -R 777 /var/www/
concede permisos excesivos y puede facilitar modificaciones no autorizadas.
En aplicaciones dinámicas debemos identificar los directorios que realmente necesitan escritura.
Por ejemplo, Laravel utiliza directorios como:
storage/
bootstrap/cache/
Debemos otorgar permisos específicos únicamente donde corresponda.
19. Configurar un dominio real
Ahora imaginemos que tenemos un VPS y un dominio registrado.
Utilizaremos:
Dominio: ejemplo.com
IP: 203.0.113.10
La IP mostrada está reservada para documentación. Debemos reemplazarla por la IP pública real del servidor.
Configurar DNS
En el proveedor del dominio creamos un registro A:
Tipo: A
Nombre: @
Valor: 203.0.113.10
Para www podemos crear:
Tipo: CNAME
Nombre: www
Valor: ejemplo.com
Si utilizamos IPv6, los registros AAAA también deben estar correctamente configurados.
Comprobar DNS
dig +short ejemplo.com A
Y:
dig +short www.ejemplo.com
El DNS permite que los visitantes encuentren la dirección del servidor.
Nginx se encarga posteriormente de seleccionar el sitio correspondiente.
20. Crear el Server Block del dominio real
Creamos:
sudo mkdir -p /var/www/ejemplo.com/public
Agregamos nuestra página:
sudo nano /var/www/ejemplo.com/public/index.html
Contenido:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Mi sitio con Nginx</title>
</head>
<body>
<h1>Bienvenido a ejemplo.com</h1>
<p>Sitio publicado con Nginx en Debian 13.</p>
</body>
</html>
Creamos la configuración:
sudo nano /etc/nginx/sites-available/ejemplo.com
Contenido:
server {
listen 80;
server_name ejemplo.com www.ejemplo.com;
root /var/www/ejemplo.com/public;
index index.html;
location / {
try_files $uri $uri/ =404;
}
access_log /var/log/nginx/ejemplo.com-access.log;
error_log /var/log/nginx/ejemplo.com-error.log;
}
Habilitamos:
sudo ln -s \
/etc/nginx/sites-available/ejemplo.com \
/etc/nginx/sites-enabled/ejemplo.com
Validamos:
sudo nginx -t
Recargamos:
sudo systemctl reload nginx
Ahora el sitio debería responder por HTTP si el DNS, la red y el firewall están correctamente configurados.
21. Configurar el firewall
En producción debemos permitir solamente los puertos necesarios.
Instalamos UFW:
sudo apt install ufw
Antes de habilitarlo en un VPS remoto debemos permitir SSH.
Si utilizamos el puerto habitual:
sudo ufw allow 22/tcp
Permitimos HTTP:
sudo ufw allow 80/tcp
Permitimos HTTPS:
sudo ufw allow 443/tcp
Consultamos:
sudo ufw status verbose
Finalmente:
sudo ufw enable
Advertencia: si configuramos incorrectamente las reglas de SSH podemos perder el acceso remoto al VPS.
También debemos revisar el firewall del proveedor y cualquier regla adicional de red.
22. Instalar HTTPS con Let’s Encrypt
HTTPS permite cifrar las conexiones entre el navegador y nuestro servidor.
Podemos obtener certificados mediante Let’s Encrypt y administrarlos con Certbot.
Instalamos:
sudo apt install certbot python3-certbot-nginx
Antes de solicitar el certificado debemos comprobar:
- Que el dominio apunte a nuestro VPS.
- Que Nginx responda correctamente.
- Que el puerto 80 esté accesible.
- Que los nombres solicitados estén configurados en el Server Block.
Solicitamos el certificado:
sudo certbot --nginx -d ejemplo.com -d www.ejemplo.com
Certbot intentará verificar el control de los dominios y configurar HTTPS.
Si la validación es correcta, podremos visitar:
https://ejemplo.com
Comprobar la renovación
sudo certbot renew --dry-run
También podemos consultar los temporizadores:
systemctl list-timers | grep certbot
Los certificados deben renovarse antes de vencer.
Conviene supervisar su vigencia y comprobar periódicamente que la renovación automática funciona.
23. Comprender una configuración HTTPS
Un bloque HTTPS utiliza habitualmente el puerto 443.
Ejemplo ilustrativo:
server {
listen 443 ssl;
server_name ejemplo.com www.ejemplo.com;
root /var/www/ejemplo.com/public;
index index.html;
ssl_certificate /etc/letsencrypt/live/ejemplo.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ejemplo.com/privkey.pem;
location / {
try_files $uri $uri/ =404;
}
}
No debemos habilitar este bloque antes de que existan los certificados correspondientes.
Si utilizamos Certbot con el complemento de Nginx, normalmente puede administrar parte de esta configuración automáticamente.
ssl_certificate
Indica la ubicación del certificado y su cadena.
ssl_certificate_key
Indica la ubicación de la clave privada.
La clave privada nunca debe colocarse dentro del directorio público.
Redirigir HTTP hacia HTTPS
Una vez que HTTPS funciona correctamente, podemos utilizar un bloque HTTP para redirigir al visitante:
server {
listen 80;
server_name ejemplo.com www.ejemplo.com;
return 301 https://$host$request_uri;
}
Esta configuración debe adaptarse al comportamiento de dominios y redirecciones que deseamos utilizar.
24. Configuraciones básicas de seguridad
Nginx dispone de varias directivas que ayudan a reducir información expuesta y controlar solicitudes.
Reducir información de versión
Podemos establecer en el contexto http:
server_tokens off;
Esto evita que Nginx anuncie su número de versión en determinadas respuestas.
No elimina todas las formas de identificar el software ni sustituye las actualizaciones.
Limitar el tamaño de solicitudes
client_max_body_size 10m;
Establece un límite de 10 MiB para el cuerpo de la solicitud.
Es útil para controlar determinadas cargas, pero debemos adaptarlo si la aplicación permite subir archivos grandes.
Evitar servir archivos sensibles
Podemos utilizar reglas como:
location ~ /\.(?!well-known/) {
deny all;
}
También podemos proteger determinadas extensiones:
location ~* \.(?:bak|old|sql|ini|log)$ {
deny all;
}
Estas reglas deben probarse según la estructura del sitio.
La medida más importante es mantener los archivos privados fuera del directorio público.
25. Configurar cabeceras HTTP de seguridad
Podemos establecer determinadas cabeceras en Nginx.
Por ejemplo:
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "SAMEORIGIN" always;
Estas cabeceras ayudan a controlar determinados comportamientos de los navegadores.
Debemos tener en cuenta que la herencia de add_header depende del contexto y de la versión de Nginx.
Si definimos cabeceras adicionales en bloques internos, podemos necesitar repetir las cabeceras relevantes.
HSTS
Una vez que HTTPS esté correctamente configurado, podemos considerar:
add_header Strict-Transport-Security "max-age=31536000" always;
Esta cabecera debe enviarse únicamente mediante HTTPS.
No debemos activarla sin verificar que podremos mantener HTTPS durante el periodo indicado.
Tampoco conviene incluir subdominios sin comprobar que todos funcionan correctamente con TLS.
26. Comprender las ubicaciones location
Una de las características más importantes de Nginx es el uso de bloques location.
Ejemplo:
location / {
try_files $uri $uri/ =404;
}
Este bloque procesa solicitudes que coinciden con la ubicación general.
También podemos utilizar ubicaciones específicas.
Por ejemplo:
location /imagenes/ {
expires 30d;
}
Esto establece una política de expiración para las respuestas que coinciden con esa ubicación.
Sin embargo, debemos considerar cómo se actualizan los archivos y qué encabezados necesita la aplicación.
¿Qué significa location ~?
El símbolo ~ indica que se utiliza una expresión regular sensible a mayúsculas y minúsculas.
Ejemplo:
location ~ \.php$ {
}
¿Qué significa location ~*?
Indica una expresión regular que no distingue entre mayúsculas y minúsculas.
Ejemplo:
location ~* \.(jpg|jpeg|png|webp)$ {
}
¿Qué significa location =?
Indica una coincidencia exacta.
Ejemplo:
location = /favicon.ico {
access_log off;
}
La selección de ubicaciones en Nginx tiene reglas de prioridad.
No debemos asumir que simplemente utiliza el primer bloque que aparece en el archivo.
27. Configurar Nginx para WordPress
WordPress utiliza PHP y normalmente MySQL o MariaDB.
En Nginx necesitamos configurar correctamente las rutas amigables.
Un ejemplo básico:
server {
listen 80;
server_name blog.ejemplo.com;
root /var/www/blog.ejemplo.com/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
location ~ /\.(?!well-known/) {
deny all;
}
}
¿Por qué utilizamos try_files?
try_files $uri $uri/ /index.php?$args;
Primero intenta encontrar el archivo solicitado.
Después comprueba si corresponde a un directorio.
Si no existe, envía la solicitud al controlador frontal de WordPress.
Esto permite que WordPress procese rutas como:
https://blog.ejemplo.com/mi-articulo/
Nginx no utiliza .htaccess, por lo que las reglas de Apache no pueden copiarse directamente.
La instalación completa de WordPress también requiere configurar la base de datos, los permisos, HTTPS y las medidas de seguridad correspondientes.
28. Configurar Nginx para Laravel
Laravel utiliza un directorio público llamado:
public/
Por ejemplo:
/var/www/mi-proyecto/
├── app/
├── bootstrap/
├── config/
├── public/
├── resources/
├── routes/
├── storage/
└── vendor/
El directorio público es:
/var/www/mi-proyecto/public
Nuestro bloque puede comenzar así:
server {
listen 80;
server_name laravel.test;
root /var/www/mi-proyecto/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
location ~ /\.(?!well-known/) {
deny all;
}
access_log /var/log/nginx/laravel-access.log;
error_log /var/log/nginx/laravel-error.log;
}
Esta es una base didáctica. Para producción conviene revisar la configuración recomendada por la versión concreta de Laravel y proteger explícitamente los archivos que no deben ejecutarse o entregarse directamente.
Configurar Laravel para producción
En el archivo .env:
APP_ENV=production
APP_DEBUG=false
También debemos establecer correctamente APP_URL, las credenciales de base de datos, la caché, las colas y los demás servicios utilizados.
Laravel necesita permisos de escritura en directorios como:
storage/
bootstrap/cache/
Debemos conceder esos permisos únicamente a la identidad que ejecuta la aplicación.
No debemos utilizar chmod 777 para solucionar problemas de escritura.
29. Configurar Nginx como proxy inverso
Además de entregar archivos, Nginx puede actuar como intermediario entre el navegador y una aplicación.
Por ejemplo, imaginemos una aplicación que escucha localmente en:
127.0.0.1:3000
Podemos configurar:
server {
listen 80;
server_name aplicacion.test;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
}
¿Qué hace proxy_pass?
Envía la solicitud a otro servicio.
En este ejemplo, Nginx recibe la conexión HTTP y la transmite a la aplicación que escucha en el puerto 3000.
¿Para qué sirven proxy_set_header?
Permiten enviar información relevante al servicio de destino.
Por ejemplo, el nombre del host original o la dirección IP del cliente.
La aplicación debe configurar correctamente qué proxies considera confiables.
No debe confiar indiscriminadamente en encabezados enviados por clientes externos.
Esta arquitectura se utiliza habitualmente con aplicaciones Node.js, Python y otros servicios HTTP.
30. Configurar un sitio predeterminado seguro
Cuando Nginx recibe una solicitud que no coincide con los nombres configurados, utiliza un servidor predeterminado para la dirección y puerto correspondientes.
Podemos definir explícitamente ese comportamiento.
Por ejemplo, para HTTP:
server {
listen 80 default_server;
server_name _;
return 444;
}
El código 444 es una extensión de Nginx que cierra la conexión sin enviar una respuesta HTTP convencional.
Esto puede ser útil para rechazar solicitudes dirigidas a nombres no reconocidos.
Sin embargo, debemos comprobar que no exista otro bloque default_server para el mismo puerto.
También debemos configurar el comportamiento predeterminado de HTTPS de forma independiente, teniendo en cuenta TLS y los certificados.
31. Consultar los registros de Nginx
Los registros suelen encontrarse en:
/var/log/nginx/
Consultar errores
sudo tail -f /var/log/nginx/error.log
Consultar accesos
sudo tail -f /var/log/nginx/access.log
Consultar un sitio específico
sudo tail -f /var/log/nginx/empresa.test-error.log
Los registros permiten detectar errores, solicitudes fallidas y problemas de configuración.
Códigos HTTP frecuentes
| Código | Significado |
|---|---|
| 200 | Solicitud correcta |
| 301 | Redirección permanente |
| 302 | Redirección temporal |
| 403 | Acceso prohibido |
| 404 | Recurso no encontrado |
| 500 | Error interno |
| 502 | Bad Gateway |
| 503 | Servicio no disponible |
| 504 | Gateway Timeout |
Un error 502 puede aparecer cuando Nginx no logra comunicarse correctamente con un servicio de destino, como PHP-FPM.
Un error 504 puede indicar que el servicio intermediario no recibió una respuesta a tiempo.
32. Solucionar errores comunes
Nginx no inicia
Validamos:
sudo nginx -t
Consultamos:
sudo systemctl status nginx
Y:
sudo journalctl -u nginx -n 50 --no-pager
El puerto 80 está ocupado
sudo ss -ltnp '( sport = :80 )'
Debemos comprobar si Apache, otro Nginx o un contenedor está utilizando el puerto.
Aparece la página predeterminada
Comprobamos los sitios habilitados:
ls -l /etc/nginx/sites-enabled/
Y revisamos:
sudo nginx -T
Puede existir un bloque predeterminado que está recibiendo la solicitud.
Error 403 Forbidden
Revisamos permisos y directorios superiores:
namei -l /var/www/empresa.test/public/index.html
También comprobamos reglas deny, ubicaciones y existencia del documento índice.
Error 404 Not Found
Comprobamos que el recurso exista dentro del directorio configurado en root.
También revisamos try_files.
Error 502 Bad Gateway
Comprobamos PHP-FPM:
systemctl status php8.4-fpm
Revisamos el socket:
ls -l /run/php/
Y consultamos:
sudo journalctl -u php8.4-fpm --since "15 minutes ago"
También debemos verificar que fastcgi_pass apunte al socket o dirección correctos.
33. Mejorar el rendimiento de Nginx
Nginx permite ajustar diferentes parámetros.
Por ejemplo, en su configuración principal podemos encontrar:
worker_processes auto;
Esto permite que Nginx determine automáticamente el número de procesos trabajadores según los recursos disponibles.
También encontraremos un bloque events:
events {
worker_connections 768;
}
worker_connections controla el número máximo de conexiones simultáneas que puede abrir cada proceso trabajador, sujeto a otros límites del sistema.
No debemos asumir que multiplicar estos valores siempre mejora el rendimiento.
También debemos considerar:
- Memoria disponible.
- Límites de descriptores de archivos.
- PHP-FPM.
- Base de datos.
- Conexiones activas.
- Tiempo de respuesta de las aplicaciones.
Compresión
Nginx puede comprimir determinados tipos de contenido.
Ejemplo:
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
text/plain
text/css
application/javascript
application/json
application/xml;
Estas directivas pueden colocarse en el contexto adecuado, como http.
La compresión debe configurarse considerando compatibilidad, consumo de CPU y riesgos asociados a comprimir respuestas sensibles.
Caché de recursos estáticos
Podemos utilizar:
location ~* \.(?:css|js|png|jpg|jpeg|gif|webp|svg)$ {
expires 7d;
}
Esto puede reducir solicitudes repetidas.
Sin embargo, si modificamos un archivo conservando el mismo nombre, algunos visitantes podrían seguir utilizando la versión almacenada en caché.
Para aplicaciones modernas conviene utilizar nombres de archivos versionados o con hashes cuando sea posible.
34. Mantener Nginx actualizado
Como cualquier servicio expuesto a Internet, Nginx debe mantenerse actualizado.
Consultamos:
apt list --upgradable
Actualizamos:
sudo apt update
sudo apt upgrade
En producción debemos revisar los cambios, mantener respaldos y comprobar el funcionamiento de las aplicaciones después de actualizar.
También conviene mantener actualizados PHP, las bases de datos, los frameworks y los gestores de contenido.
35. Lista de comprobación antes de publicar
Antes de considerar nuestro sitio preparado para producción, revisamos:
- Nginx inicia correctamente.
nginx -tno muestra errores.- Cada dominio tiene su bloque
server. - Los directorios públicos son correctos.
- El DNS apunta al servidor correspondiente.
- El firewall permite únicamente los servicios necesarios.
- HTTPS funciona.
- La renovación de certificados está configurada.
- PHP-FPM funciona cuando se necesita.
- Los archivos privados no están expuestos.
- Los permisos son adecuados.
- Los registros pueden consultarse.
- Las aplicaciones están actualizadas.
- Existen respaldos y procedimientos de recuperación.
- El servidor dispone de monitoreo.
Esta lista es una base operativa. No sustituye una auditoría de seguridad ni garantiza que el servidor esté libre de vulnerabilidades.
36. Resumen de comandos importantes
| Comando | Función |
|---|---|
sudo apt install nginx | Instalar Nginx |
systemctl status nginx | Consultar estado |
sudo systemctl restart nginx | Reiniciar |
sudo systemctl reload nginx | Recargar configuración |
sudo nginx -t | Validar configuración |
sudo nginx -T | Mostrar configuración cargada |
sudo systemctl enable nginx | Habilitar inicio automático |
sudo tail -f /var/log/nginx/error.log | Consultar errores |
sudo certbot --nginx | Gestionar HTTPS con Certbot |
systemctl status php8.4-fpm | Consultar PHP-FPM |
37. Conclusión
Nginx es una herramienta muy útil para administrar servidores web en Linux.
Durante este tutorial aprendimos a instalarlo en Debian 13, comprender sus archivos de configuración y alojar múltiples páginas mediante bloques server.
También configuramos dominios locales utilizando /etc/hosts, revisamos la publicación con dominios reales, instalamos PHP-FPM y aprendimos a configurar HTTPS mediante Let’s Encrypt.
Además, estudiamos directivas importantes como server_name, root, location, try_files, fastcgi_pass y proxy_pass.
Estos conocimientos permiten comenzar a administrar sitios estáticos, aplicaciones PHP y servicios publicados mediante proxy inverso.
Sin embargo, debemos recordar que instalar y configurar Nginx es solamente una parte de la administración de un servidor de producción.
También necesitamos proteger SSH, aislar aplicaciones cuando corresponda, establecer respaldos, supervisar recursos y mantener actualizados los servicios.
Un servidor correctamente administrado no solamente debe responder a las solicitudes: también debe poder mantenerse, protegerse y recuperarse ante fallas.
OPERSLINUX — Aprende Linux comprendiendo cómo funciona.
Comprender la configuración de Nginx nos permite avanzar desde un laboratorio local hacia la administración de servidores web reales, con una base sólida para continuar aprendiendo seguridad, automatización y despliegue de aplicaciones.



