Instalar y configurar nginx en Debian y Ubuntu
Del paquete apt al primer bloque server con PHP-FPM y HTTPS: qué versión de nginx trae cada distribución, qué cambia con el repositorio de nginx.org y cómo librarse de los errores más típicos.
Instalar nginx lleva treinta segundos. El resto del día se va en que el bloque server no surte efecto, en que PHP devuelve un 502 o en que Apache ya ocupa el puerto 80. Este artículo repasa exactamente esos puntos, y lo hace por separado para Debian 13, Debian 12, Ubuntu 24.04 y Ubuntu 22.04, porque los cuatro sistemas se comportan de forma distinta en varios aspectos.
Qué nginx elegir: paquete de la distribución o repositorio oficial
La primera decisión se toma antes del primer comando. Cada distribución incluye una versión congelada que ya solo recibe parches de seguridad. A fecha de julio de 2026, el panorama es este:
| Sistema | nginx del paquete de la distribución |
| Debian 13 (trixie) | 1.26.3 |
| Debian 12 (bookworm) | 1.22.1 |
| Ubuntu 24.04 LTS (noble) | 1.24.0 |
| Ubuntu 22.04 LTS (jammy) | 1.18.0 |
En el propio nginx.org, en julio de 2026 están disponibles las ramas 1.30.4 (stable) y 1.31.3 (mainline). La distancia respecto a Ubuntu 22.04 equivale así a unos seis años de desarrollo de funciones.
Elige el paquete de la distribución si gestionas páginas web clásicas o montajes de reverse proxy y quieres que unattended-upgrades haga el trabajo por ti. Elige el repositorio de nginx.org si necesitas HTTP/3 y QUIC (no existen en Ubuntu 22.04 y tampoco en Debian 12), si quieres módulos dinámicos ya compilados como nginx-module-brotli o el módulo ACME, o si quieres usar exactamente la misma versión en varias distribuciones.
Lo que muchas guías callan: los dos paquetes no son el mismo programa en versiones distintas, están compilados de forma distinta y empaquetados de forma distinta. Esa es la causa más frecuente de que una guía copiada no funcione.
| Característica | Paquete de Debian/Ubuntu | Paquete de nginx.org |
| Usuario del proceso | www-data | nginx |
| Docroot predeterminado | /var/www/html | /usr/share/nginx/html |
| sites-available y sites-enabled | disponibles | no existen |
| /etc/nginx/snippets/ | disponible | no existe |
| Perfiles de ufw (Nginx Full, etc.) | disponibles | no existen |
| Módulos dinámicos | libnginx-mod-* | nginx-module-* |
Instalación desde el paquete de la distribución
Idéntica en los cuatro sistemas:
apt update
apt install -y nginx
apt install -y curl
El metapaquete nginx es suficiente. nginx-full y nginx-extras siguen existiendo en Debian 12 y 13, pero solo arrastran paquetes de módulos adicionales. Los módulos sueltos los instalas de forma selectiva, por ejemplo con apt install libnginx-mod-http-headers-more-filter.
curl aparece ahí a propósito. No es una dependencia de nginx y falta en un Debian o un Ubuntu recién instalados, incluso después de que nginx se haya instalado sin problemas. Sin ese paso, el primer comando de comprobación de más abajo se interrumpe con curl: command not found, y la misma trampa afecta después a la prueba de la cabecera Host y a la prueba de PHP.
Ahora viene la parte que la mayoría de las guías se saltan: la comprobación de que realmente está funcionando. Un apt install sin mensaje de error no demuestra nada.
nginx -v
systemctl is-enabled nginx
systemctl is-active nginx
curl -I http://127.0.0.1/
Lo esperado es enabled, active y un HTTP/1.1 200 OK con una cabecera Server que mencione nginx (Debian 13 responde con Server: nginx y Ubuntu 24.04 con Server: nginx/1.24.0). Solo entonces el servidor web está realmente en pie. Si quieres saber con qué opciones se compiló el paquete, usa nginx -V (con V mayúscula); ahí también se indica si --with-http_v3_module está incluido.
Si ufw está activo, todavía falta la regla del firewall. Antes, una mirada al propio paquete: en Ubuntu Server, ufw viene instalado de fábrica y solo está inactivo; en Debian no está presente en absoluto. Ahí, la primera llamada responde con ufw: command not found. Instálalo por tanto también, el paquete existe en los cuatro sistemas:
apt install -y ufw
ufw app list
ufw allow 'Nginx Full'
Los perfiles los aporta el paquete nginx-common de la distribución: Nginx Full, Nginx HTTP y Nginx HTTPS, y en Debian 13 además Nginx QUIC. Si has instalado desde el repositorio de nginx.org, ni siquiera aparecen en ufw app list (véase la tabla de arriba).
Añadir el repositorio oficial de nginx
nginx.org da soporte a bookworm, trixie, jammy y noble. apt-key está obsoleto: la clave va en un keyring propio y se referencia mediante signed-by.
apt install -y curl gnupg2 ca-certificates lsb-release debian-archive-keyring
En Ubuntu, el último paquete se llama ubuntu-keyring en lugar de debian-archive-keyring. Después:
mkdir -p /root/.gnupg && chmod 700 /root/.gnupg
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg
El mkdir de la primera línea no es relleno. En un servidor recién instalado, /root/.gnupg todavía no existe, y el gpg 2.4.7 de Debian 13 no crea el directorio por su cuenta con esta combinación exacta de opciones. El comando de comprobación se interrumpe entonces con gpg: Fatal: /root/.gnupg: directory does not exist!, y lo hace en mitad de la salida, es decir, antes de que aparezcan las huellas digitales. Quien quiera verlas tiene que crear el directorio de antemano; como alternativa basta con ejecutar gpg --list-keys una sola vez.
El último comando tampoco es un adorno, es la verificación de verdad. Muestra tres claves, y eso es intencionado y no un motivo para abandonar: la clave de firma actual 8540 A6F1 8833 A80E 9C16 53A4 2FD2 1310 B49F 6B46 (signing-key-2@nginx.com), además de 573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62 y 9E9B E90E ACBC DE69 FE9B 204C BCDC D8A3 8D88 A2B3. Si ahí aparecen otras huellas digitales, la descarga ha entregado algo distinto de lo esperado y debes detenerte en este punto.
Ahora la fuente de paquetes. Fíjate en el segmento de la ruta que va después de /packages/, porque nginx.org mantiene dos árboles de directorios separados para Debian y para Ubuntu. Bajo /packages/debian/dists/ no existen ni noble ni jammy: los paquetes de Ubuntu están exclusivamente bajo /packages/ubuntu/. Esta versión determina el segmento por sí misma y por eso funciona sin cambios en ambas familias:
OS=$(. /etc/os-release; echo $ID)
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/$OS $(lsb_release -cs) nginx" | tee /etc/apt/sources.list.d/nginx.list
$ID vale debian en Debian y ubuntu en Ubuntu, así que la ruta se ajusta automáticamente a la distribución. Si en su lugar escribes fijo packages/debian porque estás siguiendo una guía de Debian sobre un Ubuntu, echo escribe la línea sin rechistar y el error no llega hasta el siguiente apt update:
Err: http://nginx.org/packages/debian noble Release
E: The repository 'http://nginx.org/packages/debian noble Release' does not have a Release file.
En Ubuntu 22.04 aparece ahí jammy en lugar de noble, pero la causa es la misma. Para la rama mainline se antepone mainline/ al nombre de la distribución. Y para que apt prefiera realmente el paquete de nginx.org frente al de la distribución hace falta un pinning; de lo contrario gana el equivocado según cuál sea el número de versión:
printf 'Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n' | tee /etc/apt/preferences.d/99nginx
apt update
apt-cache policy nginx
apt install -y nginx
apt-cache policy nginx es el control previo a la instalación: como Candidate tiene que figurar la versión de nginx.org (por ejemplo 1.30.4-1~noble) y como prioridad el 900 del archivo de pin. Si ahí sigue apareciendo la versión de la distribución, el pinning no está surtiendo efecto e instalarías directamente el paquete equivocado.
Cambiar desde una instalación existente de la distribución
Si ya está funcionando el paquete de la distribución, la actualización falla. El mensaje viene a decir esto:
dpkg: error processing archive /var/cache/apt/archives/nginx_1.30.4-1~bookworm_amd64.deb (--unpack):
trying to overwrite '/etc/nginx/mime.types', which is also in package nginx-common 1.22.1-9+deb12u9
El paquete de nginx.org no conoce ningún nginx-common, por eso los archivos colisionan. La vía limpia: primero asegurar la configuración, después eliminar el paquete de la distribución y por último instalar de nuevo.
tar czf /root/nginx-config-backup.tar.gz /etc/nginx
systemctl stop nginx
apt purge -y nginx nginx-common
apt install -y nginx
Que tar muestre por el camino tar: Removing leading '/' from member names es normal y no es un error. Después, /etc/nginx/sites-available ha desaparecido y tus vHosts antiguos solo existen ya dentro del tarball. Cópialos a /etc/nginx/conf.d/ y dales la extensión .conf, porque si no, no se cargan. Ten en cuenta dos cosas: include snippets/fastcgi-php.conf; no existe en el paquete de nginx.org, y el usuario del proceso ahora se llama nginx, lo que afecta a los permisos de los archivos y a los sockets de PHP-FPM.
Entender bien sites-available y sites-enabled
El modelo de dos directorios es un invento puramente de Debian, nginx en sí no lo conoce. En /etc/nginx/sites-available/ están todos los archivos de configuración y en /etc/nginx/sites-enabled/ hay enlaces simbólicos a los que están activos. Solo se carga aquello que aparece en nginx.conf mediante include. En caso de duda, compruébalo tú mismo:
grep include /etc/nginx/nginx.conf
En el paquete de Debian y Ubuntu hay ahí dos líneas, include /etc/nginx/conf.d/*.conf; e include /etc/nginx/sites-enabled/*;. En el paquete de nginx.org solo está la primera. De ahí viene justamente el clásico: alguien sigue una guía de Ubuntu sobre un paquete de nginx.org, crea /etc/nginx/sites-available/mi-sitio, pone el enlace simbólico, obtiene un syntax is ok impecable con nginx -t y, aun así, no pasa nada. No hay ningún mensaje de error, porque ese directorio sencillamente no le interesa a nadie.
La contraprueba que siempre dice la verdad es nginx -T con T mayúscula. Muestra la configuración completa ya resuelta, es decir, exactamente lo que nginx ve de verdad:
nginx -T | grep -n "server_name\|listen\|root"
Si tu server_name no aparece ahí, el archivo no se está leyendo. Punto. Seguir depurando la configuración en sí es entonces una pérdida de tiempo.
El segundo error frecuente es un enlace simbólico que apunta al vacío, por ejemplo tras una errata o porque el archivo de destino se ha renombrado:
nginx: [emerg] open() "/etc/nginx/sites-enabled/mi-sitio" failed (2: No such file or directory) in /etc/nginx/nginx.conf:62
Los enlaces simbólicos rotos los encuentras con find /etc/nginx/sites-enabled/ -xtype l. Y un sitio nunca se desactiva borrando el archivo en sites-available, sino quitando el enlace simbólico con unlink /etc/nginx/sites-enabled/mi-sitio.
Tu primer bloque server propio
Vamos a crear un sitio estático. Primero el directorio y un archivo de prueba:
mkdir -p /var/www/ejemplo/html
echo '<h1>Página de prueba de KernelHost</h1>' > /var/www/ejemplo/html/index.html
chown -R www-data:www-data /var/www/ejemplo
En el paquete de nginx.org, el propietario es nginx:nginx. Ahora la configuración, y de verdad como un paso aparte: el archivo tiene que existir antes de que el enlace simbólico apunte a él. Créalo con el editor o escríbelo directamente mediante un heredoc; en el paquete de nginx.org va en su lugar a /etc/nginx/conf.d/ejemplo.conf:
cat > /etc/nginx/sites-available/ejemplo <<'EOF'
server {
listen 80;
listen [::]:80;
server_name ejemplo.com www.ejemplo.com;
root /var/www/ejemplo/html;
index index.html;
access_log /var/log/nginx/ejemplo.access.log;
error_log /var/log/nginx/ejemplo.error.log;
location / {
try_files $uri $uri/ =404;
}
}
EOF
Las comillas simples alrededor de 'EOF' son importantes: sin ellas, la shell sustituye $uri por nada y el bloque ya queda roto al guardarlo. Solo después toca activar, comprobar y recargar:
ln -s /etc/nginx/sites-available/ejemplo /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx
El orden no es arbitrario. ln crea el enlace simbólico incluso cuando el archivo de destino no existe, y sin decir una palabra. Eso solo sale a la luz en la comprobación:
nginx: [emerg] open() "/etc/nginx/sites-enabled/ejemplo" failed (2: No such file or directory) in /etc/nginx/nginx.conf:61
El número de línea varía según el sistema (61 en Debian 13, y 60 en Debian 12 y en los dos Ubuntu), pero el mensaje es idéntico. Un reload se rechazaría en ese estado, así que la configuración en ejecución queda intacta. Este caso concreto también lo detecta find /etc/nginx/sites-enabled/ -xtype l.
En este punto aparecen una y otra vez otros tres errores.
Servidor predeterminado duplicado. Quien copia default_server de una guía aunque el sitio predeterminado de Debian siga activo, obtiene esto:
nginx: [emerg] a duplicate default server for 0.0.0.0:80 in /etc/nginx/sites-enabled/ejemplo:2
O bien omites la palabra clave, o bien desactivas el sitio predeterminado con unlink /etc/nginx/sites-enabled/default.
Nombre de servidor duplicado. Si el mismo nombre aparece en dos bloques, gana sin más comentarios el que se carga primero y solo se emite un aviso:
nginx: [warn] conflicting server name "ejemplo.com" on 0.0.0.0:80, ignored
Nombre de servidor demasiado largo. Con dominios largos o con muchos subdominios:
nginx: [emerg] could not build server_names_hash, you should increase server_names_hash_bucket_size: 32
La solución es server_names_hash_bucket_size 64; en el bloque http de nginx.conf.
La comprobación de funcionamiento se hace sin DNS, a través de la cabecera Host:
curl -H 'Host: ejemplo.com' -sS http://127.0.0.1/
Si aparece el sitio predeterminado de Debian en lugar de tu página de prueba, tu bloque no está surtiendo efecto y acabas en el servidor predeterminado. Si aparece tu página, todo está en orden.
Integrar PHP-FPM
Aquí acecha la trampa más molesta de toda la guía, y le cuesta una hora a mucha gente. El metapaquete php depende, a través de php8.x, de la alternativa libapache2-mod-php8.x | php8.x-fpm | php8.x-cgi. apt elige siempre la primera, así que apt install php instala también Apache sin falta, y Apache ocupa después el puerto 80. Instala por eso de forma selectiva:
apt install -y php-fpm php-mysql php-xml php-curl php-mbstring php-zip
Qué versión de PHP obtienes y cómo se llama el socket depende de la distribución:
| Sistema | PHP | Socket | Servicio |
| Debian 13 | 8.4 | /run/php/php8.4-fpm.sock | php8.4-fpm |
| Debian 12 | 8.2 | /run/php/php8.2-fpm.sock | php8.2-fpm |
| Ubuntu 24.04 | 8.3 | /run/php/php8.3-fpm.sock | php8.3-fpm |
| Ubuntu 22.04 | 8.1 | /run/php/php8.1-fpm.sock | php8.1-fpm |
No adivines la ruta, léela:
ls -l /run/php/
El bloque de PHP dentro del server (ejemplo con Debian 12, adapta la ruta):
index index.php index.html;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.(?!well-known).* {
deny all;
}
En Debian y Ubuntu puedes sustituir las primeras líneas por include snippets/fastcgi-php.conf;. En el paquete de nginx.org ese snippet no existe, así que ahí necesitas la versión escrita por completo. El try_files $uri =404; no es un recurso de estilo: impide que se ejecuten archivos subidos que llevan un .php añadido en la ruta.
Pero todavía falta un paso, y casi siempre se omite. La llamada de prueba que viene ahora pasa por el vhost predeterminado que se entrega de fábrica, y en /etc/nginx/sites-available/default la sección location ~ \.php$ está comentada por completo de serie. Mientras siga así, nginx entrega los archivos .php sin modificar: obtienes un 200 OK con Content-Type: application/octet-stream y el código fuente de PHP en texto plano. Esto no es un defecto estético. En un archivo real, en ese punto hay credenciales de base de datos o claves de API, y las lee cualquiera que conozca la URL.
Así que primero hay que activar el bloque. En /etc/nginx/sites-available/default, quita los caracteres de comentario que lo preceden o escríbelo entero:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
El nombre del socket depende de la distribución y aparece en la tabla de arriba: php8.4 en Debian 13, php8.3 en Ubuntu 24.04, php8.2 en Debian 12 y php8.1 en Ubuntu 22.04. Después, comprobar y recargar:
nginx -t
systemctl reload nginx
Solo ahora tiene valor la comprobación de que PHP se ejecuta realmente a través de FPM y de que no se está ofreciendo simplemente la descarga del archivo:
printf '<?php echo "PHP ", PHP_VERSION, " via ", php_sapi_name(), "\n";' > /var/www/html/kh-check.php
curl -s http://127.0.0.1/kh-check.php
rm /var/www/html/kh-check.php
Lo correcto es una salida del estilo PHP 8.4.23 via fpm-fcgi, y en Ubuntu 24.04 la equivalente PHP 8.3.6 via fpm-fcgi. Si en su lugar vuelve el código fuente, location ~ \.php$ todavía no está surtiendo efecto en el vhost activo, es decir, sigue comentado o apunta al socket equivocado. No olvides borrar el archivo, y no uses phpinfo() en un servidor accesible desde fuera.
Leer bien un 502 Bad Gateway
Un 502 no es un diagnóstico; el diagnóstico está en /var/log/nginx/error.log. Hay tres líneas típicas:
connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory) while connecting to upstream
Ruta equivocada o FPM no está en ejecución. Compruébalo con systemctl status php8.2-fpm y ls /run/php/. Un desencadenante frecuente es una actualización de distribución: tras el salto de Debian 12 a 13, la configuración antigua sigue apuntando a php8.2-fpm.sock, pero lo instalado es la 8.4.
connect() to unix:/run/php/php8.2-fpm.sock failed (13: Permission denied) while connecting to upstream
Esto afecta casi en exclusiva a instalaciones del repositorio de nginx.org. El pool de FPM pertenece a www-data y tiene el modo 0660, pero ahí nginx se ejecuta como usuario nginx. Pon en /etc/php/8.2/fpm/pool.d/www.conf el valor listen.group = nginx y reinicia FPM.
FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream
SCRIPT_FILENAME apunta al vacío. Lo habitual es que root esté en el bloque location en lugar de en el bloque server, o que falte por completo.
HTTPS con Let's Encrypt
Las versiones de certbot de las distribuciones están muy separadas: Debian 13 incluye la 4.0.0, Ubuntu 24.04 la 2.9.0, Debian 12 la 2.1.0 y Ubuntu 22.04 solo la 1.21.0. Todas hablan ACMEv2, pero para funciones más recientes, como los certificados de vida corta, la 1.21 se queda anticuada. En Ubuntu 22.04 merece la pena el rodeo por snap.
apt install -y certbot python3-certbot-nginx
certbot --version
Antes de la emisión hay que cumplir tres condiciones; si no, la validación falla sin un mensaje aprovechable: el registro A (y, si corresponde, el AAAA) apunta al servidor, el puerto 80 es accesible desde fuera y existe un bloque server con el server_name correspondiente. El último punto es decisivo, porque el plugin de nginx localiza el bloque a través de server_name, no a través del nombre del archivo. Después:
certbot --nginx -d ejemplo.com -d www.ejemplo.com
A continuación, certbot escribe en tu archivo de configuración: un segundo bloque server con listen 443 ssl, las rutas a fullchain.pem y privkey.pem, un include /etc/letsencrypt/options-ssl-nginx.conf y una redirección desde el puerto 80. Que el archivo se modifique sorprende con frecuencia. Si después lo sobrescribes a mano, pierdes el HTTPS y en la siguiente recarga obtienes:
nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/ejemplo.com/fullchain.pem": BIO_new_file() failed
La renovación automática se hace mediante un timer de systemd, no mediante un cron job. Comprueba las dos cosas:
systemctl list-timers | grep certbot
certbot renew --dry-run
La ejecución en seco es la única prueba real de que la renovación funcionará dentro de noventa días. Si usas el repositorio de nginx.org, desde nginx 1.29 existe además con nginx-module-acme una variante completamente sin certbot, en la que nginx solicita y renueva los certificados por su cuenta.
nginx -t, reload y restart
La regla es sencilla y aun así se incumple constantemente: nunca hagas un reload sin un nginx -t previo. Ante un error de sintaxis, nginx se niega a adoptar la configuración rota, pero con un restart el proceso antiguo ya se ha terminado y el sitio queda fuera de línea.
nginx -t
Lo esperado es exactamente esto:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
reload arranca workers nuevos con la nueva configuración y deja que los antiguos terminen de atender sus conexiones abiertas. No se pierde ni una sola petición. restart solo lo necesitas cuando cambia algo del proceso en sí, por ejemplo después de una actualización de paquetes, con un user modificado o al cargar directivas load_module.
Si el arranque falla, la salida de systemd resulta casi inútil:
Job for nginx.service failed because the control process exited with error code.
See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.
El texto aprovechable está un nivel más abajo:
journalctl -xeu nginx.service --no-pager -n 30
Resolver el conflicto de puerto con Apache en el puerto 80
El error de nginx más conocido de todos:
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Ten en cuenta que nginx -t no detecta este error, porque la prueba no vincula ningún puerto. Solo falla el arranque. Averigua primero quién retiene el puerto, en lugar de adivinar:
apt install -y iproute2
ss -tlnp | grep ':80'
La salida indica el proceso en texto claro, normalmente users:(("apache2",pid=612,fd=4)). En nueve de cada diez casos, Apache entró en el sistema a través de apt install php o de algún panel. Hay tres salidas limpias.
Primera, apagar Apache. Si no lo necesitas, basta con desactivarlo para que no vuelva tras el siguiente reinicio:
systemctl disable --now apache2
systemctl start nginx
Segunda, eliminar Apache. Atención: si libapache2-mod-php depende de él, apt purge apache2 puede llevarse PHP por delante. Revisa la lista que apt muestra antes de ejecutar y después instala php-fpm.
Tercera, ejecutar los dos en paralelo. Tiene sentido cuando los vHosts de Apache existentes con .htaccess deben seguir funcionando y nginx trabaja delante como reverse proxy. Para ello pones en /etc/apache2/ports.conf un Listen 127.0.0.1:8080, cambias en cada vHost <VirtualHost *:80> por <VirtualHost *:8080> y dejas que nginx reenvíe:
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
Para que Apache deje de ver únicamente 127.0.0.1 en sus logs, activa ahí el módulo remoteip. Sin ese paso, las reglas de Fail2ban sobre los logs de Apache no valen nada, porque toda petición parece venir de localhost.
Dos variantes del mismo error se pasan por alto con facilidad. Si la misma entrada listen aparece por duplicado en dos archivos tuyos, nginx también informa de Address already in use, aunque Apache no tenga nada que ver. Y en el caso de bind() to [::]:80 failed solo está afectado IPv6, normalmente porque un segundo bloque establece también listen [::]:80 sin ipv6only=on.
La aceptación final: ocho comprobaciones en lugar de intuición
Antes de dar una instalación por terminada, repasa estos puntos. Cada uno de ellos ha evitado alguna vez un fallo silencioso.
nginx -tinforma de test is successful.systemctl is-enabled nginxdevuelveenabled, así que el servicio vuelve después de un reinicio.ss -tlnp | grep nginxmuestra el puerto 80 y, si lo has configurado, el puerto 443.nginx -T | grep server_namelista todos los dominios que deben funcionar.curl -I http://127.0.0.1/devuelve un 200 o una redirección intencionada.- El archivo de prueba de PHP muestra
fpm-fcgiy después se ha borrado. certbot renew --dry-runse ejecuta sin errores./var/log/nginx/error.logno contiene entradas nuevas después de un acceso de prueba.
En un servidor recién instalado falta después el firewall; para eso consulta nuestra guía sobre la configuración de ufw. Debian 10 y Ubuntu 20.04 están sin soporte desde junio de 2024 y mayo de 2025 respectivamente, y ya no reciben actualizaciones de seguridad de nginx; ahí una actualización no es una cuestión de comodidad, sino algo que se debería haber hecho hace tiempo.
Lo que aporta KernelHost
En los servidores root KVM y en los servidores dedicados de KernelHost instalas Debian 13, Debian 12, Ubuntu 24.04 o Ubuntu 22.04 directamente desde el área de cliente y después tienes acceso root completo, de modo que los pasos descritos arriba funcionan sin cambios. Los sistemas están en el centro de datos maincubes de Frankfurt am Main (Alemania), certificado por TÜV según TIER3+, y conectados a través de nuestra propia red. La protección DDoS actúa delante del servidor y no dentro de nginx: 3,2 Tbps de filtrado Arbor en tiempo real directamente en la ubicación y, en las tarifas Professional, además hasta 17 Tbps de capacidad de filtrado global. Todo funciona en régimen PrePaid, es decir, sin permanencia mínima, sin plazo de preaviso y sin cuota de alta.
Preguntas frecuentes
¿Debo instalar nginx desde el paquete de la distribución o desde el repositorio oficial de nginx.org?
¿Por qué se ignora por completo mi bloque server en sites-available aunque nginx -t termine con éxito?
nginx no arranca e informa de bind() to 0.0.0.0:80 failed (98: Address already in use). ¿Qué hago?
¿Qué versión de PHP y qué socket de FPM necesito en mi distribución?
¿Cómo compruebo de forma fiable que nginx funciona de verdad y que no se ha ejecutado solo el comando?
2026 KernelHost GmbH. Todos los derechos reservados. Esta guía está protegida por derechos de autor. Su publicación en otros sitios web, aunque sea de forma parcial o modificada, no está permitida sin nuestro consentimiento por escrito. Las citas con indicación de la fuente y un enlace son muy bienvenidas.

