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