Express: Раздача статики

express.static — встроенный middleware Express для раздачи статических файлов (HTML, CSS, JS, изображений) напрямую из указанной директории без написания маршрутов вручную.

Зачем нужно

Без express.static пришлось бы вручную читать файлы через fs и отправлять их с правильными заголовками Content-Type. Middleware берёт это на себя: автоматически устанавливает MIME-типы, заголовки кеширования (ETag, Last-Modified, Cache-Control), обрабатывает условные GET-запросы (304 Not Modified) и поддерживает range-запросы для медиафайлов.

Где используется

  • SPA (React, Vue, Angular) — раздача собранного dist/ или build/
  • Документация — HTML-файлы, сгенерированные JSDoc, Docusaurus
  • Загруженные пользователем файлы — аватары, документы (через Multer)
  • Assets API-сервера — favicon, robots.txt, OpenAPI-спецификация

Основной контент

Базовое использование

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

// Раздавать файлы из папки public/
app.use(express.static(path.join(__dirname, 'public')));

// public/index.html доступен как GET /
// public/css/style.css доступен как GET /css/style.css
// public/images/logo.png доступен как GET /images/logo.png

С виртуальным префиксом

// Файлы из папки uploads/ доступны по /files/...
app.use('/files', express.static(path.join(__dirname, 'uploads')));

// GET /files/avatar.png → отдаст uploads/avatar.png

Опции кеширования

app.use(express.static(path.join(__dirname, 'public'), {
  maxAge: '1d',         // Cache-Control: max-age=86400
  etag: true,           // ETag для условных запросов (по умолчанию true)
  lastModified: true,   // Last-Modified заголовок (по умолчанию true)
  index: 'index.html',  // Файл по умолчанию для директорий
  dotfiles: 'ignore',   // Скрыть .env, .htaccess и т.п.
}));

Несколько директорий

// Поиск файла сначала в public/, потом в assets/
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'assets')));

SPA — отдавать index.html для всех маршрутов

// Сначала статика
app.use(express.static(path.join(__dirname, 'dist')));

// Потом API маршруты
app.use('/api', require('./routes'));

// Для всех остальных GET — отдать index.html (SPA routing)
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});

Заголовки безопасности для статики

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

app.use(helmet); // Добавит CSP, X-Frame-Options и др.
app.use(express.static(path.join(__dirname, 'public'), {
  setHeaders(res, filePath) {
    if (filePath.endsWith('.html')) {
      res.setHeader('Cache-Control', 'no-cache');
    }
  }
}));

Частые ошибки

  • Относительный путь вместо абсолютногоexpress.static('public') зависит от CWD процесса; всегда используй path.join(__dirname, 'public')
  • Раздача папки uploads без проверки — пользователи могут получить доступ к чужим файлам; использовать виртуальные пути и проверять права
  • Статика после API-роутеров — если маршруты /api/users объявлены после express.static, это нормально; если до — статика может перехватить запрос к файлу с таким же именем
  • Кешировать HTML-файлы — браузер закеширует старый index.html; для HTML ставь Cache-Control: no-cache

Связанные темы

Ресурсы