Express

Express

Tutorial Express: Servir archivos estáticos

Aprende a configurar express.static() para servir archivos estáticos en Node.js con rutas virtuales y opciones avanzadas.

Aprende Express y certifícate

Configuración express.static()

El middleware express.static() es la función integrada de Express que permite servir archivos estáticos directamente desde el servidor. Este middleware elimina la necesidad de crear rutas específicas para cada archivo estático, proporcionando una solución eficiente y automática para entregar recursos como CSS, JavaScript, imágenes y documentos.

Sintaxis básica

La configuración más simple de express.static() requiere únicamente especificar la carpeta que contiene los archivos estáticos:

const express = require('express');
const app = express();

// Configuración básica para servir archivos estáticos
app.use(express.static('public'));

app.listen(3000, () => {
  console.log('Servidor ejecutándose en puerto 3000');
});

Con esta configuración, Express servirá automáticamente cualquier archivo ubicado en la carpeta public. Si tienes un archivo public/styles.css, estará disponible en http://localhost:3000/styles.css.

Configuración con rutas virtuales

Express permite crear rutas virtuales que no corresponden directamente con la estructura de carpetas del sistema de archivos. Esto proporciona mayor flexibilidad en la organización de URLs:

// Crear una ruta virtual '/assets' para la carpeta 'public'
app.use('/assets', express.static('public'));

// Crear múltiples rutas virtuales
app.use('/css', express.static('public/stylesheets'));
app.use('/js', express.static('public/javascripts'));
app.use('/images', express.static('public/img'));

Con esta configuración, un archivo public/logo.png estaría disponible en http://localhost:3000/assets/logo.png, mientras que public/stylesheets/main.css se accedería mediante http://localhost:3000/css/main.css.

Opciones de configuración avanzada

El middleware express.static() acepta un objeto de opciones como segundo parámetro para personalizar su comportamiento:

const path = require('path');

// Configuración con opciones avanzadas
app.use('/static', express.static(path.join(__dirname, 'public'), {
  dotfiles: 'ignore',        // Ignorar archivos que empiecen con punto
  etag: false,              // Deshabilitar generación de ETags
  extensions: ['htm', 'html'], // Extensiones a buscar automáticamente
  index: ['index.html', 'default.html'], // Archivos índice por defecto
  maxAge: '1d',             // Tiempo de caché en el navegador
  redirect: false,          // No redirigir cuando la URL termina en '/'
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}));

Múltiples directorios estáticos

Express permite configurar múltiples directorios estáticos que se evalúan en el orden de registro. Esto resulta útil para organizar diferentes tipos de recursos:

// Configurar múltiples directorios en orden de prioridad
app.use(express.static('public'));
app.use(express.static('assets'));
app.use(express.static('uploads'));

// Con rutas virtuales específicas
app.use('/vendor', express.static('node_modules'));
app.use('/uploads', express.static('user-uploads'));

Cuando Express recibe una solicitud de archivo estático, busca en cada directorio configurado hasta encontrar el archivo solicitado, siguiendo el orden de registro.

Configuración con rutas absolutas

Para evitar problemas de rutas relativas, especialmente en aplicaciones complejas, es recomendable usar rutas absolutas con el módulo path:

const path = require('path');

// Usar rutas absolutas para mayor seguridad
app.use(express.static(path.join(__dirname, 'public')));
app.use('/uploads', express.static(path.join(__dirname, 'storage', 'uploads')));

// Configuración para diferentes entornos
const staticPath = process.env.NODE_ENV === 'production' 
  ? path.join(__dirname, 'dist') 
  : path.join(__dirname, 'public');

app.use(express.static(staticPath));

Configuración condicional por entorno

En aplicaciones profesionales, la configuración de archivos estáticos suele variar según el entorno de ejecución:

// Configuración específica por entorno
if (process.env.NODE_ENV === 'development') {
  // En desarrollo: sin caché, con logs detallados
  app.use(express.static('public', {
    maxAge: 0,
    etag: false,
    setHeaders: (res, path) => {
      console.log(`Sirviendo archivo: ${path}`);
    }
  }));
} else {
  // En producción: con caché optimizado
  app.use(express.static('dist', {
    maxAge: '30d',
    etag: true,
    immutable: true
  }));
}

Esta configuración permite optimizar el rendimiento en producción mientras mantiene la flexibilidad necesaria durante el desarrollo.

Estructura de carpetas públicas

La organización de carpetas públicas en Express requiere una planificación cuidadosa para mantener el código escalable y facilitar el mantenimiento. Una estructura bien definida no solo mejora la experiencia del desarrollador, sino que también optimiza el rendimiento del servidor y la carga de recursos en el navegador.

Estructura básica recomendada

La estructura estándar para archivos estáticos en Express sigue convenciones establecidas que facilitan la navegación y el mantenimiento:

proyecto/
├── public/
│   ├── css/
│   │   ├── main.css
│   │   ├── components/
│   │   │   ├── navbar.css
│   │   │   └── footer.css
│   │   └── vendor/
│   │       └── bootstrap.min.css
│   ├── js/
│   │   ├── app.js
│   │   ├── modules/
│   │   │   ├── auth.js
│   │   │   └── utils.js
│   │   └── vendor/
│   │       └── jquery.min.js
│   ├── images/
│   │   ├── logo.png
│   │   ├── icons/
│   │   │   ├── favicon.ico
│   │   │   └── apple-touch-icon.png
│   │   └── gallery/
│   │       ├── thumb/
│   │       └── full/
│   ├── fonts/
│   │   ├── roboto-regular.woff2
│   │   └── icons.ttf
│   └── docs/
│       ├── manual.pdf
│       └── api-docs.html

Esta estructura separa claramente los diferentes tipos de recursos, facilitando tanto el desarrollo como el despliegue de la aplicación.

Organización por funcionalidad

Para aplicaciones más complejas, la organización por módulos o funcionalidades puede resultar más eficiente:

// Configuración modular de archivos estáticos
app.use('/admin', express.static(path.join(__dirname, 'public/admin')));
app.use('/client', express.static(path.join(__dirname, 'public/client')));
app.use('/shared', express.static(path.join(__dirname, 'public/shared')));
public/
├── admin/
│   ├── css/
│   │   └── dashboard.css
│   ├── js/
│   │   └── admin-panel.js
│   └── images/
│       └── admin-icons/
├── client/
│   ├── css/
│   │   └── storefront.css
│   ├── js/
│   │   └── shopping-cart.js
│   └── images/
│       └── products/
└── shared/
    ├── css/
    │   └── common.css
    ├── js/
    │   └── utilities.js
    └── fonts/

Gestión de versiones de archivos

La versionado de archivos estáticos es crucial para el control de caché en navegadores. Express permite implementar diferentes estrategias:

// Estructura con versionado manual
app.use('/v1', express.static(path.join(__dirname, 'public/v1')));
app.use('/v2', express.static(path.join(__dirname, 'public/v2')));

// Estructura con hash en nombres de archivo
app.use(express.static(path.join(__dirname, 'dist'), {
  setHeaders: (res, path) => {
    // Archivos con hash tienen caché largo
    if (path.match(/\.[a-f0-9]{8}\./)) {
      res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
    }
  }
}));
dist/
├── css/
│   ├── main.a1b2c3d4.css
│   └── vendor.e5f6g7h8.css
├── js/
│   ├── app.i9j0k1l2.js
│   └── chunk.m3n4o5p6.js
└── images/
    ├── logo.q7r8s9t0.png
    └── sprite.u1v2w3x4.svg

Separación de entornos

La configuración por entornos permite optimizar la entrega de archivos según el contexto de ejecución:

const isDevelopment = process.env.NODE_ENV === 'development';
const isProduction = process.env.NODE_ENV === 'production';

if (isDevelopment) {
  // Desarrollo: archivos sin procesar
  app.use(express.static(path.join(__dirname, 'src/assets')));
  app.use('/dev-tools', express.static(path.join(__dirname, 'dev')));
}

if (isProduction) {
  // Producción: archivos optimizados
  app.use(express.static(path.join(__dirname, 'build/static')));
}

// Archivos comunes a todos los entornos
app.use('/uploads', express.static(path.join(__dirname, 'storage/uploads')));

Organización de archivos subidos por usuarios

Los archivos generados dinámicamente requieren una estructura específica que facilite su gestión y seguridad:

storage/
├── uploads/
│   ├── avatars/
│   │   ├── 2024/
│   │   │   ├── 01/
│   │   │   └── 02/
│   │   └── thumbs/
│   ├── documents/
│   │   ├── public/
│   │   └── private/
│   └── temp/
│       └── processing/
// Configuración para archivos de usuario
app.use('/uploads/avatars', express.static(
  path.join(__dirname, 'storage/uploads/avatars'),
  {
    maxAge: '7d',
    setHeaders: (res, filePath) => {
      // Validar que el archivo existe y es accesible
      res.setHeader('X-Content-Type-Options', 'nosniff');
    }
  }
));

// Archivos temporales con acceso restringido
app.use('/temp', (req, res, next) => {
  // Lógica de autorización aquí
  if (!req.user || !req.user.canAccessTemp) {
    return res.status(403).send('Acceso denegado');
  }
  next();
}, express.static(path.join(__dirname, 'storage/temp')));

Optimización de estructura para CDN

Cuando se utiliza un CDN (Content Delivery Network), la estructura debe facilitar la sincronización y distribución:

public/
├── cdn/
│   ├── css/
│   ├── js/
│   ├── images/
│   └── fonts/
├── local/
│   ├── templates/
│   └── config/
└── hybrid/
    ├── critical.css
    └── above-fold.js
// Configuración híbrida CDN/local
const cdnEnabled = process.env.CDN_ENABLED === 'true';

if (!cdnEnabled) {
  // Servir desde servidor local cuando CDN no está disponible
  app.use('/cdn', express.static(path.join(__dirname, 'public/cdn')));
}

// Archivos que siempre se sirven localmente
app.use('/local', express.static(path.join(__dirname, 'public/local')));
app.use('/critical', express.static(path.join(__dirname, 'public/hybrid')));

Esta aproximación permite flexibilidad en el despliegue y garantiza que la aplicación funcione correctamente independientemente de la disponibilidad del CDN.

Aprende Express online

Otras lecciones de Express

Accede a todas las lecciones de Express y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Express y certifícate

Ejercicios de programación de Express

Evalúa tus conocimientos de esta lección Servir archivos estáticos con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.