0%
Montar un servidor web en Linux: Nginx y Apache

Montar un servidor web en Linux: Nginx y Apache

Guía completa para desplegar un servidor web Linux: Nginx o Apache, virtual hosts, HTTPS con Let's Encrypt, reverse proxy, seguridad y optimizaciones.

I

InSkillCoach

· min

Montar un servidor web en Linux: Nginx y Apache

Alojar un sitio o una aplicación web en su propio servidor Linux es uno de los ejercicios más formativos de la administración de sistemas: implica la gestión de paquetes, los servicios systemd, la red, los certificados TLS y la seguridad. En esta guía vamos a montar un servidor web completo, desde la instalación hasta el HTTPS pasando por el reverse proxy, cubriendo los dos servidores que dominan el mercado: Nginx y Apache. Los ejemplos utilizan Ubuntu/Debian; las diferencias con la familia Red Hat se indican cuando existen.

1. Elegir Nginx o Apache

Ambos servidores son maduros, eficientes y perfectamente capaces de alojar cualquier sitio. Sin embargo, sus arquitecturas son distintas y algunos contextos favorecen a uno u otro.

CriterioNginxApache
ArquitecturaOrientada a eventos, asíncronaProcesos/hilos por conexión (MPM event moderno)
Contenido estáticoExcelente, consumo de memoria muy bajoBueno
Conexiones simultáneas masivasPunto fuerte históricoCorrecto con mpm_event
Reverse proxy / load balancingNativo y muy utilizadoPosible (mod_proxy)
Archivos .htaccessNo soportadosSoportados (configuración por directorio)
Ejecución de código (PHP…)Siempre delegada (FastCGI/PHP-FPM)Posible mediante módulos integrados (mod_php)
Sintaxis de configuraciónBloques concisosDirectivas de tipo XML, más verbosas
Caso de uso típicoReverse proxy, estático, aplicaciones modernasAlojamiento compartido, legacy, .htaccess necesario

En 2026, la opción por defecto para un proyecto nuevo suele ser Nginx: reverse proxy nativo, configuración compacta y consumo de memoria estable bajo carga elevada. Apache sigue siendo imprescindible cuando sus usuarios deben modificar la configuración por directorio (.htaccess) o para aplicaciones históricas que dan por hecho su uso. El resto de la guía se centra en Nginx e indica los equivalentes de Apache en los pasos clave.

2. Instalación

Nginx

# Debian / Ubuntu
sudo apt update
sudo apt install nginx

# RHEL / Rocky / Alma
sudo dnf install nginx
sudo systemctl enable --now nginx

# Comprobar que el servicio está en marcha
systemctl status nginx
curl -I http://localhost

Apache

# Debian / Ubuntu (el paquete y el servicio se llaman apache2)
sudo apt install apache2

# RHEL / Rocky / Alma (el paquete y el servicio se llaman httpd)
sudo dnf install httpd
sudo systemctl enable --now httpd

No olvide abrir el cortafuegos:

# Ubuntu con ufw
sudo ufw allow "Nginx Full"

# RHEL con firewalld
sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reload

3. Estructura de la configuración

Nginx

/etc/nginx/
├── nginx.conf              # configuración principal
├── conf.d/                 # archivos .conf incluidos automáticamente
├── sites-available/        # sus vhosts (Debian/Ubuntu)
├── sites-enabled/          # enlaces simbólicos a los vhosts activos
└── snippets/               # fragmentos reutilizables (SSL, etc.)

En Debian/Ubuntu, cada sitio se escribe en sites-available/ y después se activa mediante un enlace simbólico en sites-enabled/. En RHEL esta convención no existe: coloque sus archivos directamente en conf.d/.

# Activar un sitio (Debian/Ubuntu)
sudo ln -s /etc/nginx/sites-available/monsite.conf /etc/nginx/sites-enabled/

# Desactivar un sitio: eliminar el enlace, nunca el archivo de origen
sudo rm /etc/nginx/sites-enabled/monsite.conf

Apache

/etc/apache2/                     # Debian/Ubuntu (RHEL: /etc/httpd/)
├── apache2.conf                  # configuración principal
├── sites-available/              # vhosts disponibles
├── sites-enabled/                # vhosts activos
├── mods-available/               # módulos disponibles
└── mods-enabled/                 # módulos activos

Apache proporciona comandos específicos para la activación:

sudo a2ensite monsite.conf        # activar un vhost
sudo a2dissite monsite.conf       # desactivar
sudo a2enmod rewrite ssl headers  # activar módulos

4. Virtual hosts y server blocks

Un mismo servidor puede alojar varios sitios, distinguidos por su nombre de dominio. Nginx habla de «server blocks» y Apache de «virtual hosts».

Server block completo de Nginx

# /etc/nginx/sites-available/monsite.conf
server {
    listen 80;
    listen [::]:80;

    server_name monsite.example.com;

    root /var/www/monsite;
    index index.html index.htm;

    # Registros dedicados al sitio
    access_log /var/log/nginx/monsite.access.log;
    error_log  /var/log/nginx/monsite.error.log;

    location / {
        # Servir el archivo; si no existe, el directorio; si no, 404
        try_files $uri $uri/ =404;
    }

    # Bloquear el acceso a los archivos ocultos (.git, .env...)
    location ~ /\. {
        deny all;
    }
}
# Crear la raíz del sitio y una página de prueba
sudo mkdir -p /var/www/monsite
echo "<h1>Bienvenue sur monsite</h1>" | sudo tee /var/www/monsite/index.html
sudo chown -R www-data:www-data /var/www/monsite

# Activar y recargar
sudo ln -s /etc/nginx/sites-available/monsite.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Virtual host equivalente de Apache

# /etc/apache2/sites-available/monsite.conf
<VirtualHost *:80>
    ServerName monsite.example.com
    DocumentRoot /var/www/monsite

    ErrorLog ${APACHE_LOG_DIR}/monsite.error.log
    CustomLog ${APACHE_LOG_DIR}/monsite.access.log combined

    <Directory /var/www/monsite>
        Options -Indexes
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
sudo a2ensite monsite.conf
sudo apachectl configtest && sudo systemctl reload apache2

5. HTTPS con Let’s Encrypt

Un sitio sin HTTPS ya no es aceptable: los navegadores y los motores de búsqueda penalizan el HTTP. Let’s Encrypt proporciona certificados gratuitos, y certbot automatiza todo el proceso, desde el desafío de validación hasta la renovación.

# Instalación de certbot con el plugin de Nginx
sudo apt install certbot python3-certbot-nginx
# (Apache: python3-certbot-apache; RHEL: mediante EPEL)

# Obtener el certificado Y modificar la configuración automáticamente
sudo certbot --nginx -d monsite.example.com

# Comprobar la renovación automática (timer de systemd)
systemctl list-timers | grep certbot
sudo certbot renew --dry-run

Requisitos previos: el dominio debe apuntar a la IP pública del servidor (registro DNS A/AAAA) y el puerto 80 debe ser accesible para el desafío HTTP-01. Certbot añade la redirección de HTTP a HTTPS y configura los certificados en su server block:

# Fragmento añadido por certbot en el server block
listen 443 ssl;
ssl_certificate     /etc/letsencrypt/live/monsite.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/monsite.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;

Los certificados de Let’s Encrypt son válidos durante 90 días; el timer de systemd instalado por el paquete los renueva automáticamente al acercarse la fecha de caducidad. Basta con ejecutar el --dry-run una vez tras la instalación.

6. Reverse proxy hacia una aplicación

Es un caso extremadamente habitual: su aplicación (Node.js, Python, Go…) escucha en un puerto local, y Nginx la publica en el puerto 443 encargándose de TLS, la compresión y los logs.

# /etc/nginx/sites-available/app.conf
# Agrupación de los backends (permite hacer load balancing más adelante)
upstream mon_app {
    server 127.0.0.1:3000;
    keepalive 32;
}

server {
    listen 443 ssl;
    server_name app.example.com;

    ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

    location / {
        proxy_pass http://mon_app;

        # Transmitir a la aplicación la información del cliente
        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;

        # Soporte de WebSockets
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Tiempos de espera razonables
        proxy_connect_timeout 5s;
        proxy_read_timeout    60s;
    }
}

Las cabeceras X-Forwarded-* son esenciales: sin ellas, su aplicación ve todas las peticiones como procedentes de 127.0.0.1 y cree estar en HTTP. En la familia Red Hat, piense también en el booleano de SELinux que autoriza a Nginx a abrir conexiones salientes:

sudo setsebool -P httpd_can_network_connect on

7. Cabeceras de seguridad

Algunas cabeceras HTTP protegen a sus visitantes contra el clickjacking, el sniffing MIME y las degradaciones a HTTP. Deben colocarse en el server block (o en un snippet incluido por todos sus sitios):

# /etc/nginx/snippets/security-headers.conf
# HSTS: fuerza HTTPS durante 2 años (actívelo solo cuando HTTPS sea estable)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;

# Prohíbe la inclusión del sitio en un iframe de terceros (clickjacking)
add_header X-Frame-Options "SAMEORIGIN" always;

# Prohíbe al navegador adivinar los tipos MIME
add_header X-Content-Type-Options "nosniff" always;

# Limita la información de procedencia transmitida a sitios de terceros
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# En cada server block HTTPS
include snippets/security-headers.conf;

Añada una Content-Security-Policy adaptada a su sitio cuando conozca con precisión sus fuentes de scripts y estilos: es la cabecera más eficaz, pero un ajuste demasiado estricto rompe el sitio. Pruebe su configuración en securityheaders.com y la parte TLS en ssllabs.com.

Oculte también la versión del servidor, una información inútil que no conviene ofrecer a los escáneres:

# En http {} de /etc/nginx/nginx.conf
server_tokens off;

8. Logs y rotación

Leer los registros

# Seguir el tráfico en tiempo real
sudo tail -f /var/log/nginx/access.log

# Seguir los errores
sudo tail -f /var/log/nginx/error.log

# Top 10 de las IP más activas
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head

# Contar los códigos de respuesta
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

Rotación con logrotate

Los paquetes de Nginx y Apache ya instalan una configuración de logrotate; compruébela y adapte la retención a sus necesidades:

# /etc/logrotate.d/nginx (proporcionado por el paquete, fragmento)
/var/log/nginx/*.log {
    daily              # rotación diaria
    rotate 14          # conservar 14 archivos
    compress           # comprimir los archivos
    delaycompress      # excepto el más reciente
    notifempty
    sharedscripts
    postrotate
        invoke-rc.d nginx rotate >/dev/null 2>&1
    endscript
}

# Probar sin esperar a la noche
sudo logrotate -d /etc/logrotate.d/nginx

Si crea registros por sitio como en nuestros ejemplos, el patrón *.log los cubre automáticamente.

9. Probar y recargar sin cortes

Un reflejo que conviene interiorizar definitivamente: no reinicie nunca un servidor web sin haber validado la configuración, y prefiera siempre la recarga al reinicio.

# 1. Validar la sintaxis y la coherencia de la configuración
sudo nginx -t
# Apache: sudo apachectl configtest

# 2. Recargar: los workers terminan sus peticiones en curso,
#    y se inician nuevos workers con la nueva configuración.
#    No se corta ninguna conexión.
sudo systemctl reload nginx

# En una sola línea, la recarga condicionada a la prueba
sudo nginx -t && sudo systemctl reload nginx

systemctl restart corta bruscamente las conexiones en curso; resérvelo para los casos en que la recarga no sea suficiente (cambio de puertos de escucha, actualización del binario).

10. Optimizaciones básicas

Compresión gzip

# En http {} de /etc/nginx/nginx.conf
gzip on;
gzip_comp_level 5;              # buen compromiso CPU / tamaño
gzip_min_length 256;            # inútil por debajo de 256 bytes
gzip_types text/plain text/css application/json application/javascript
           application/xml image/svg+xml;
# Las imágenes JPEG/PNG/WebP ya están comprimidas: no las incluya

Caché de archivos estáticos

Pida a los navegadores que conserven durante mucho tiempo los recursos que no cambian:

# En el server block
location ~* \.(css|js|jpg|jpeg|png|webp|svg|woff2)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
    access_log off;             # también aligera los registros
}

Esta estrategia supone que sus archivos cambian de nombre en cada versión (hash en el nombre, como hacen todos los bundlers modernos). De lo contrario, reduzca la duración.

Procesos workers

# En /etc/nginx/nginx.conf
worker_processes auto;          # un worker por núcleo de CPU: el ajuste correcto

events {
    worker_connections 1024;    # conexiones simultáneas por worker
}

auto es el valor por defecto de los paquetes recientes y sirve para la práctica totalidad de los casos. Aumente worker_connections solo si sus registros muestran que se alcanza el límite, y piense entonces en el límite de descriptores de archivo (worker_rlimit_nofile).

Buenas prácticas

  • Un archivo de configuración por sitio, activado mediante enlace simbólico; no toque los archivos proporcionados por el paquete.
  • Valide con nginx -t o apachectl configtest antes de cada recarga, idealmente en la misma línea de comandos.
  • Prefiera reload a restart: cero cortes para sus visitantes.
  • HTTPS en todas partes, redirección de HTTP a HTTPS y un certbot renew --dry-run tras la instalación para validar la renovación automática.
  • Añada las cabeceras de seguridad en un snippet compartido y pruebe el resultado en securityheaders.com.
  • Registros dedicados por sitio, rotación verificada y un ojo atento a error.log.
  • Detrás de un reverse proxy, transmita siempre Host, X-Forwarded-For y X-Forwarded-Proto a la aplicación.
  • Haga copia de seguridad de /etc/nginx (o /etc/apache2) y de /etc/letsencrypt: la configuración de un servidor web se reconstruye en minutos cuando está versionada.

Conclusión

Ya dispone de un servidor web completo: varios sitios en virtual hosts, HTTPS automático con Let’s Encrypt, reverse proxy hacia sus aplicaciones, cabeceras de seguridad, registros controlados y optimizaciones básicas. Nginx y Apache son programas de una fiabilidad notable: bien configurados, funcionan durante años sin intervención. Los siguientes pasos de su progresión: el load balancing entre varios backends, el almacenamiento en caché con proxy_cache, la limitación de tasa (limit_req) contra los abusos y la automatización de toda esta configuración con Ansible o Terraform para reconstruir su servidor con un solo comando.

InSkillCoach

Acerca de InSkillCoach

Experto en formación y tecnologías

Coach especializado en tecnologías avanzadas e IA, impulsado por GNeurone Inc.

Certificaciones:

  • AWS Certified Solutions Architect – Professional
  • Certificaciones de Google Cloud
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
1.3k
197

Comentarios

Los comentarios funcionan con GitHub Discussions

Inicia sesión con GitHub para participar en la discusión

¡Enlace copiado!