Publicación de contenido web con Apache: DocumentRoot, permisos y tipos MIME

Capítulo 4

Tiempo estimado de lectura: 8 minutos

+ Ejercicio

DocumentRoot: dónde vive tu sitio

DocumentRoot es la ruta del sistema de archivos desde la que Apache publica contenido para un VirtualHost. Todo lo que esté dentro de esa carpeta (y permitido por la configuración) puede ser servido al navegador como archivos estáticos (HTML, CSS, imágenes, JS, etc.).

Objetivo práctico: definir una carpeta de sitio limpia, con permisos correctos, un index predecible y tipos MIME correctos para que el navegador interprete cada archivo como corresponde.

Estructura recomendada del sitio

Una estructura simple y mantenible para un sitio estático o la parte pública de una app:

/var/www/mi-sitio/            (raíz del proyecto, no necesariamente pública) /var/www/mi-sitio/public/     (DocumentRoot: solo lo publicable) /var/www/mi-sitio/public/index.html /var/www/mi-sitio/public/assets/ /var/www/mi-sitio/public/assets/css/styles.css /var/www/mi-sitio/public/assets/img/logo.png

Recomendación clave: usa una carpeta public/ como DocumentRoot para evitar exponer por accidente archivos sensibles (por ejemplo, .env, backups, repositorios, etc.).

Guía paso a paso: crear y publicar un sitio

1) Crear carpetas y archivos de prueba

Crea la estructura y tres recursos: un HTML, un CSS y una imagen.

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

Descargar la aplicación

sudo mkdir -p /var/www/mi-sitio/public/assets/css sudo mkdir -p /var/www/mi-sitio/public/assets/img
cat > /var/www/mi-sitio/public/index.html <<'HTML' <!doctype html> <html lang="es"> <head>   <meta charset="utf-8">   <meta name="viewport" content="width=device-width, initial-scale=1">   <title>Mi sitio</title>   <link rel="stylesheet" href="/assets/css/styles.css"> </head> <body>   <h1>Hola Apache</h1>   <p>Probando HTML + CSS + imagen.</p>   <img src="/assets/img/logo.png" alt="Logo" width="160"> </body> </html> HTML
cat > /var/www/mi-sitio/public/assets/css/styles.css <<'CSS' body { font-family: system-ui, Arial, sans-serif; margin: 2rem; } h1 { color: #1f6feb; } img { border: 2px solid #ddd; padding: 6px; border-radius: 8px; } CSS

Para la imagen, puedes copiar cualquier PNG existente o generar una de prueba. Ejemplo copiando desde un lugar conocido (ajusta la ruta):

sudo cp /ruta/a/una/imagen.png /var/www/mi-sitio/public/assets/img/logo.png

2) Definir el VirtualHost con DocumentRoot

Crea un VirtualHost (nombre de archivo orientativo; puede variar según tu distro):

sudo nano /etc/apache2/sites-available/mi-sitio.conf

Contenido mínimo recomendado:

<VirtualHost *:80>     ServerName mi-sitio.local     DocumentRoot /var/www/mi-sitio/public      <Directory /var/www/mi-sitio/public>         Options -Indexes +FollowSymLinks         AllowOverride None         Require all granted         DirectoryIndex index.html index.php     </Directory>      ErrorLog ${APACHE_LOG_DIR}/mi-sitio_error.log     CustomLog ${APACHE_LOG_DIR}/mi-sitio_access.log combined </VirtualHost>
  • Options -Indexes: evita listado de directorios si falta un index (buena práctica de seguridad).
  • +FollowSymLinks: permite seguir enlaces simbólicos (útil, pero úsalo con criterio).
  • AllowOverride None: desactiva .htaccess (mejor rendimiento y control centralizado).
  • DirectoryIndex: define el orden de archivos índice.

Activa el sitio y recarga Apache:

sudo a2ensite mi-sitio.conf sudo apache2ctl configtest sudo systemctl reload apache2

Si estás usando un nombre local como mi-sitio.local, añade una entrada en tu /etc/hosts apuntando a la IP del servidor (por ejemplo, 127.0.0.1 si es local):

sudo sh -c 'echo "127.0.0.1 mi-sitio.local" >> /etc/hosts'

Permisos y propiedad: servir archivos sin abrir puertas

Usuario/grupo del servicio web

Apache suele ejecutarse como un usuario sin privilegios (por ejemplo, www-data en Debian/Ubuntu). Ese usuario necesita permisos de lectura sobre los archivos del sitio y permisos de ejecución (traversal) sobre los directorios para poder entrar en ellos.

Regla práctica: directorios 755 y archivos 644 suelen ser suficientes para contenido estático.

Diferencias entre chmod y chown

  • chmod: cambia permisos (lectura/escritura/ejecución) para propietario, grupo y otros.
  • chown: cambia propietario y/o grupo del archivo/directorio.

Ejemplo: dejar propietario a tu usuario (para editar) y grupo al del servidor (para lectura controlada), con permisos seguros:

# Ajusta TU_USUARIO y el grupo del servidor (ej. www-data) sudo chown -R TU_USUARIO:www-data /var/www/mi-sitio
# Directorios 755 y archivos 644 sudo find /var/www/mi-sitio/public -type d -exec chmod 755 {} \; sudo find /var/www/mi-sitio/public -type f -exec chmod 644 {} \;

Impacto en seguridad:

  • Evita 777: permite escritura a cualquiera; facilita defacement y escaladas si hay otra vulnerabilidad.
  • Evita que el usuario del servidor web tenga permisos de escritura en el DocumentRoot si no es estrictamente necesario (reduce el impacto de una posible ejecución de código o subida de archivos).
  • Si necesitas subidas (uploads), crea un directorio específico con permisos mínimos y controles adicionales, en lugar de abrir todo el árbol.

Comprobación rápida de permisos efectivos

Verifica permisos y propiedad:

ls -ld /var/www/mi-sitio/public ls -l /var/www/mi-sitio/public/index.html

Comprueba que el usuario del servicio puede leer (sin convertirlo en propietario). En Debian/Ubuntu, por ejemplo:

sudo -u www-data test -r /var/www/mi-sitio/public/index.html && echo OK || echo FAIL

Directiva Directory: control fino del acceso

La sección <Directory ...> define cómo Apache trata una ruta del sistema de archivos. Es el lugar adecuado para aplicar políticas de seguridad y comportamiento del contenido.

Options: qué funcionalidades se permiten

  • Indexes: si está activo y no hay DirectoryIndex, Apache muestra listado de archivos. Recomendación: desactivarlo con -Indexes.
  • FollowSymLinks: permite seguir symlinks. Útil, pero revisa que no apunten fuera de lo esperado.
  • ExecCGI: permite ejecutar CGI (no lo actives si no lo usas).

Ejemplo seguro típico para contenido estático:

<Directory /var/www/mi-sitio/public>     Options -Indexes +FollowSymLinks     Require all granted </Directory>

AllowOverride y .htaccess: cuándo permitirlo y cuándo evitarlo

.htaccess permite que reglas de Apache vivan dentro del propio directorio del sitio. Es útil en entornos compartidos o cuando no tienes acceso a la configuración global, pero tiene dos costes:

  • Rendimiento: Apache debe comprobar .htaccess en cada petición (y en cada nivel de directorio).
  • Gobernanza/seguridad: reglas críticas quedan dispersas y pueden ser modificadas por quien tenga escritura en el árbol.

Recomendación:

  • En servidores bajo tu control: usa AllowOverride None y configura todo en el VirtualHost.
  • Permite .htaccess solo si lo necesitas (por ejemplo, para reglas de reescritura en un hosting donde no puedes editar el VirtualHost), y limita el alcance.

Ejemplo permitiendo solo directivas de reescritura (si necesitas mod_rewrite):

<Directory /var/www/mi-sitio/public>     Options -Indexes +FollowSymLinks     AllowOverride FileInfo     Require all granted </Directory>

Ejemplo de .htaccess mínimo (solo demostrativo) para forzar un index alternativo o reglas de cache (evita ponerlo si puedes hacerlo en el VirtualHost):

# /var/www/mi-sitio/public/.htaccess DirectoryIndex index.html

DirectoryIndex: qué archivo se sirve por defecto

Si el usuario solicita / o un directorio, Apache busca en orden los nombres indicados en DirectoryIndex. Si no encuentra ninguno y Indexes está desactivado, devolverá 403.

Ejemplo:

DirectoryIndex index.html index.php

Tipos MIME: que el navegador interprete bien el contenido

Los tipos MIME se envían en la cabecera Content-Type. Si el servidor envía un tipo incorrecto, el navegador puede no aplicar estilos, no mostrar imágenes o bloquear recursos por políticas de seguridad.

Apache suele mapear extensiones a MIME mediante mime.types (y el módulo de MIME). En la práctica, .html debe ser text/html, .css debe ser text/css, y .png debe ser image/png.

Si necesitas forzar o añadir tipos, se puede hacer con directivas como AddType (preferiblemente en la config del sitio, no en .htaccess salvo necesidad):

<IfModule mod_mime.c>     AddType text/css .css     AddType image/png .png </IfModule>

Comprobaciones: servir HTML, CSS e imagen y validar cabeceras

1) Probar respuesta HTTP y cabeceras con curl

Comprueba que el HTML se sirve y revisa Content-Type:

curl -I http://mi-sitio.local/

Deberías ver algo similar a:

HTTP/1.1 200 OK Content-Type: text/html

Comprueba el CSS:

curl -I http://mi-sitio.local/assets/css/styles.css

Esperado:

Content-Type: text/css

Comprueba la imagen:

curl -I http://mi-sitio.local/assets/img/logo.png

Esperado:

Content-Type: image/png

2) Verificar que el contenido realmente llega

Descarga una muestra del HTML y del CSS:

curl -s http://mi-sitio.local/ | head curl -s http://mi-sitio.local/assets/css/styles.css

Para la imagen, valida que no sea una página de error HTML guardada como PNG:

curl -s -o /tmp/logo.png http://mi-sitio.local/assets/img/logo.png file /tmp/logo.png

file debería indicar algo como PNG image data.

3) Diagnóstico rápido si algo falla

SíntomaCausa típicaQué revisar
403 ForbiddenPermisos insuficientes o reglas de accesoPermisos de directorios (x) y archivos (r), sección <Directory>, Require all granted
404 Not FoundRuta incorrecta o archivo no existeDocumentRoot, rutas en el HTML (/assets/...), estructura real en disco
Se ve HTML pero no aplica CSSMIME incorrecto o ruta incorrectacurl -I al CSS, Content-Type: text/css, ubicación del archivo
Imagen rotaArchivo no accesible o tipo incorrectocurl -I a la imagen, permisos, file sobre el descargado

Ahora responde el ejercicio sobre el contenido:

En un sitio publicado con Apache, el HTML carga pero los estilos no se aplican. ¿Qué verificación es la más adecuada para confirmar si el problema es un tipo MIME incorrecto del archivo CSS?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

Si el navegador no aplica el CSS, una causa común es que el servidor envíe un Content-Type incorrecto. Consultar las cabeceras (por ejemplo, con una petición que muestre headers) permite verificar que el CSS se entregue como text/css.

Siguiente capítulo

Virtual Hosts en Apache para múltiples sitios

Arrow Right Icon
Portada de libro electrónico gratuitaApache desde Cero: Guía Práctica para Principiantes
40%

Apache desde Cero: Guía Práctica para Principiantes

Nuevo curso

10 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.