Apuntes DAM
Tema 2 de 7DAW · 2º DAW

Despliegue de Aplicaciones Web

Servidores Web: Apache y Nginx

Apache frente a Nginx, virtual hosts y bloques server, HTTPS con Let's Encrypt, cabeceras de seguridad, control de acceso, compresión y caché, y análisis de los registros, con retos de Bash.

60 min lecturaIntermedio

1. Qué hace un servidor web

Un servidor web es el programa que escucha en los puertos 80 (HTTP) y 443 (HTTPS), recibe las peticiones y decide qué hacer con cada una: devolver un fichero del disco, redirigir a otra dirección, pedir usuario y contraseña o pasar la petición a la aplicación que la sabe responder. Es la puerta de entrada de casi cualquier web, y configurarlo bien afecta a la velocidad, a la seguridad y a lo que Google opina de tu sitio.

Los dos más usados son Apache y Nginx, y entre los dos sirven la mayoría de las webs del mundo. Apache nació en 1995 y es extremadamente flexible gracias a sus módulos y a los ficheros .htaccess, que permiten cambiar la configuración carpeta a carpeta. Nginx apareció en 2004 para resolver el problema de atender miles de conexiones simultáneas con poca memoria, y hoy es la opción habitual como proxy inverso delante de las aplicaciones.

ApacheNginx
ModeloProcesos o hilos por conexión (MPM prefork, worker, event)Asíncrono, orientado a eventos: pocos procesos para miles de conexiones
Configuración por carpetaSí, con .htaccess (cómodo, pero más lento)No: todo en la configuración central
PHPIntegrado con mod_php o mediante PHP-FPMSiempre mediante PHP-FPM
Punto fuerteFlexibilidad y compatibilidad con alojamientos compartidosRendimiento con estáticos y como proxy inverso

2. Varios sitios en un servidor: los virtual hosts

Un mismo servidor, con una sola IP, puede alojar muchas webs. El truco está en la cabecera Host de cada petición HTTP, que dice a qué dominio va dirigida; el servidor la compara con sus virtual hosts (en Nginx, bloques server) y usa la configuración correspondiente.

Los virtual hosts son buzones de un mismo edificio

Un solo edificio
Una IP y un servidor para todas las webs.
La dirección del sobre
La cabecera Host indica a qué web va la petición.
Cada buzón, su casa
Cada virtual host tiene su carpeta, sus registros y su certificado.
/etc/nginx/sites-available/apuntes.es
1server {
2    listen 80;
3    server_name apuntes.es www.apuntes.es;
4    return 301 https://apuntes.es$request_uri;       # todo a HTTPS y sin www
5}
6
7server {
8    listen 443 ssl;
9    http2 on;
10    server_name apuntes.es;
11
12    ssl_certificate     /etc/letsencrypt/live/apuntes.es/fullchain.pem;
13    ssl_certificate_key /etc/letsencrypt/live/apuntes.es/privkey.pem;
14
15    root /var/www/apuntes/public;
16    index index.html index.php;
17
18    access_log /var/log/nginx/apuntes.access.log;
19    error_log  /var/log/nginx/apuntes.error.log;
20
21    location / {
22        try_files $uri $uri/ /index.php?$query_string;   # URLs amigables para un front controller
23    }
24
25    location ~ \.php$ {
26        include fastcgi_params;
27        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
28        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
29    }
30
31    location ~ /\. { deny all; }                          # nada de .env, .git…
32}

En Debian y Ubuntu, cada sitio se escribe en sites-available y se activa con un enlace simbólico en sites-enabled. Antes de recargar, comprueba siempre la sintaxis: nginx -t (o apachectl configtest) evita dejar el servidor caído por una llave olvidada. Después, systemctl reload nginx aplica los cambios sin cortar las conexiones abiertas.

/etc/apache2/sites-available/apuntes.es.conf
1<VirtualHost *:80>
2    ServerName apuntes.es
3    ServerAlias www.apuntes.es
4    DocumentRoot /var/www/apuntes/public
5
6    <Directory /var/www/apuntes/public>
7        AllowOverride All          # permite .htaccess (p. ej. las reglas de Laravel)
8        Require all granted
9    </Directory>
10
11    ErrorLog ${APACHE_LOG_DIR}/apuntes.error.log
12    CustomLog ${APACHE_LOG_DIR}/apuntes.access.log combined
13</VirtualHost>
14# a2ensite apuntes.es && a2enmod rewrite && systemctl reload apache2
Instalando y configurando Nginx - [PARTE 1]: Virtual Hosts — Pelado Nerd

3. HTTPS y certificados

HTTPS es HTTP dentro de un canal cifrado con TLS. Protege dos cosas: la confidencialidad (nadie en la red puede leer las contraseñas o las cookies) y la autenticidad (el navegador comprueba que habla con el servidor del dominio y no con un impostor). Esa comprobación se basa en un certificado: un documento que une el dominio con una clave pública, firmado por una autoridad de certificación en la que confían los navegadores.

  1. El navegador se conecta y el servidor le presenta su certificado.
  2. El navegador comprueba que está firmado por una autoridad de confianza, que no ha caducado y que es para ese dominio.
  3. Ambos acuerdan una clave de sesión con criptografía asimétrica y, a partir de ahí, cifran el tráfico con ella.

Hoy no hay excusa para no usar HTTPS: Let's Encrypt emite certificados gratuitos de 90 días y Certbot los solicita y los renueva solo. Un certbot --nginx -d apuntes.es -d www.apuntes.es obtiene el certificado y modifica la configuración; el temporizador de systemd lo renueva antes de que caduque. Aun así, conviene vigilar las fechas de caducidad, como verás en uno de los retos.

4. Seguridad, rendimiento y registros

Cabeceras de seguridad

Unas pocas cabeceras en la respuesta activan protecciones del navegador. No sustituyen a un código seguro, pero cierran puertas a ataques habituales:

nginx
1add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;  # solo HTTPS en adelante
2add_header X-Content-Type-Options "nosniff" always;
3add_header Referrer-Policy "strict-origin-when-cross-origin" always;
4add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:" always;
5server_tokens off;                                                                # no anunciar la versión

Control de acceso

Para proteger una zona (un panel de estadísticas, un entorno de pruebas) basta con autenticación básica: un fichero de usuarios creado con htpasswd y las directivas auth_basic en Nginx o AuthType Basic en Apache. También se puede limitar por IP (allow / deny) o, en empresas, autenticar contra el directorio corporativo con LDAP, como verás en el tema de servicios de red.

Rendimiento

TécnicaQué consigueCómo
CompresiónReduce el tamaño de HTML, CSS y JS hasta un 70-80 %gzip on; (o Brotli)
Caché del navegadorQue los ficheros con hash en el nombre no se vuelvan a descargarexpires 1y; o Cache-Control: max-age=31536000, immutable
HTTP/2 y HTTP/3Varias peticiones por una sola conexiónhttp2 on; en el bloque server
Estáticos desde el servidor webNo ocupar la aplicación con ficherosUna location para /assets con root y caché

Los registros cuentan lo que pasa

Cada petición deja una línea en el registro de accesos (quién, cuándo, qué pidió, qué código obtuvo y cuánto ocupó la respuesta) y cada problema, en el registro de errores. Saber leerlos con tail -f, grep y awk es la herramienta de diagnóstico más básica: un aumento de 404 puede indicar un enlace roto o alguien buscando vulnerabilidades; un aumento de 502, que la aplicación se ha caído.

Una línea del registro de Nginx

203.0.113.5 - - [30/Sep/2025:10:01:02 +0200] "GET / HTTP/1.1" 200 5120 "-" "Mozilla/5.0". Con awk, la IP es el campo 1, el método y la ruta están en los campos 6 y 7, y el código de estado en el 9.

5. Retos

Dos tareas reales de administración resueltas con Bash: sacar estadísticas de un registro de accesos y generar la configuración de un sitio a partir de sus datos.

Retos de Bash en el IDE

Escribe el script y pulsa Ejecutar tests: se ejecuta en un Linux real con Bash y se prueba con varios casos, algunos ocultos.

🖥️BashEstadísticas de un access.logMedio

La entrada es un fichero de registro de Nginx en formato combinado. Muestra «Peticiones: N», «Errores 4xx: N», «Errores 5xx: N» y «IP con más peticiones: IP (N)». Si hay empate, gana la IP menor alfabéticamente.

⏳
Registro de ejemplo
⏳
Test oculto #2
0/2 tests pasados
🖥️BashGenerar un virtual host de NginxFácil

La entrada es una línea «dominio raíz [puerto]». Genera el bloque server de Nginx con este formato exacto (4 espacios de sangría): «server {», « listen 80;», « server_name dominio www.dominio;» y, si no hay puerto, « root raíz;» y « index index.html;»; si hay puerto, en su lugar « location / {», « proxy_pass http://127.0.0.1:puerto;», « }». Termina con «}». Si falta el dominio o la raíz, escribe «Uso: dominio raíz [puerto]».

⏳
Sitio estático
⏳
Aplicación con proxy
⏳
Datos incompletos
⏳
Test oculto #4
0/4 tests pasados

¿Has terminado este tema?

Crea una cuenta gratis para guardar qué temas has terminado, subir de nivel y ganar medallas.

Guardar mi progreso