NestJSMiddleTechnical
Как реализовать rate limiting и заголовки безопасности в NestJS?
Rate limiting реализуется через @nestjs/throttler (декоратор @Throttle + ThrottlerGuard), заголовки безопасности — через пакет helmet подключённый как middleware. Оба механизма настраиваются глобально в AppModule.
Rate Limiting в NestJS
Официальный пакет @nestjs/throttler предоставляет guard и модуль для ограничения частоты запросов. Начиная с версии 5.x, модуль поддерживает несколько «throttler-ов» одновременно (например, короткое и длинное окно).
Установка
npm install @nestjs/throttler
npm install helmet
Конфигурация ThrottlerModule
// app.module.ts
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { APP_GUARD } from '@nestjs/core';
@Module({
imports: [
ThrottlerModule.forRoot([
{
name: 'short', // 10 запросов за 1 секунду
ttl: 1000,
limit: 10,
},
{
name: 'long', // 100 запросов за минуту
ttl: 60000,
limit: 100,
},
]),
],
providers: [
{
provide: APP_GUARD,
useClass: ThrottlerGuard, // глобальный rate limit
},
],
})
export class AppModule {}
Тонкая настройка на уровне маршрута
import { Throttle, SkipThrottle } from '@nestjs/throttler';
@Controller('auth')
export class AuthController {
// Жёсткий лимит для логина: 5 попыток за 60 секунд
@Throttle({ default: { ttl: 60000, limit: 5 } })
@Post('login')
login(@Body() dto: LoginDto) { ... }
// Публичный endpoint — пропускаем throttler
@SkipThrottle()
@Get('health')
health() { return 'ok'; }
}
Rate limiting с Redis (для многоподового деплоя)
По умолчанию счётчики хранятся в памяти. В многоподовом окружении нужен Redis-хранилище:
import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis';
ThrottlerModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
throttlers: [{ ttl: 60000, limit: 100 }],
storage: new ThrottlerStorageRedisService({
host: config.get('REDIS_HOST'),
port: config.get('REDIS_PORT'),
}),
}),
})
Заголовки безопасности с Helmet
Helmet устанавливает набор HTTP-заголовков, снижающих риск распространённых атак (XSS, clickjacking, MIME-sniffing и др.):
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import helmet from 'helmet';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Helmet — подключать ДО всех маршрутов
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
scriptSrc: ["'self'"],
},
},
crossOriginEmbedderPolicy: false, // отключить если нужен <iframe>
}));
// CORS
app.enableCors({
origin: process.env.ALLOWED_ORIGINS?.split(',') ?? 'http://localhost:3000',
credentials: true,
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
});
await app.listen(3000);
}
bootstrap();
Что устанавливает Helmet по умолчанию
X-Content-Type-Options: nosniff— блокирует MIME-sniffingX-Frame-Options: SAMEORIGIN— защита от clickjackingStrict-Transport-Security— принудительный HTTPS (HSTS)X-XSS-Protection: 0— отключает устаревший XSS-фильтр браузераReferrer-Policy: no-referrer— не передавать Referrer на другие доменыContent-Security-Policy— базовая политика (настраивается вручную)
Подводные камни
- Throttler по умолчанию идентифицирует клиента по IP. За reverse-proxy нужно настроить
getTrackerна чтение заголовкаX-Forwarded-For, иначе все пользователи будут идентифицированы как один IP прокси. ThrottlerGuardвозвращает429 Too Many Requests— убедитесь, что ваш frontend обрабатывает этот статус и не показывает пользователю пустой экран.- Helmet нужно подключать через
app.use(), а не через@UseInterceptorsили guard — он должен быть Express/Fastify middleware. - При использовании Fastify вместо Express:
helmetне подойдёт, нужен@fastify/helmetсapp.register(). - CSP-директива
unsafe-inlineдля скриптов нивелирует защиту от XSS — используйте nonce или hash вместо неё. - HSTS (
Strict-Transport-Security) в разработке через HTTP сломает локальный доступ — отключайте helmet или конкретную директиву в dev-окружении. - Глобальный
ThrottlerGuardприменяется и к WebSocket-шлюзам — убедитесь, что лимиты не слишком строгие для realtime-соединений.
Common mistakes
- Дает общий ответ про Node.js и не называет конкретные API NestJS.
- Не объясняет, где в lifecycle находится rate limiting и security headers.
- Не разделяет validation, authorization, business logic и persistence.
- Игнорирует ошибки, лимиты входных данных, observability и тестирование.
What the interviewer is testing
- Может объяснить rate limiting и security headers на примере кода.
- Называет ключевые API: ThrottlerModule, helmet().
- Использует точные API NestJS, а не вымышленные hooks/decorators/methods.
- Видит production-риски: безопасность, отказоустойчивость, логирование и тесты.