A NestJS API that “works on localhost” is not a production API. Before you expose a service, lock down three things that browsers and bots will hit first: CORS, rate limits, and security headers.
CORS: allow a list, not the world
Enable CORS in main.ts with an explicit origin list. Avoid origin: true on a public API unless every caller is trusted.
app.enableCors({
origin: process.env.CORS_ORIGINS?.split(",") ?? [],
methods: ["GET", "POST", "PATCH", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization"],
credentials: true,
});
If a mobile app or server-to-server client does not need cookies, drop credentials and keep the allowlist tight.
Rate limiting: fail closed
Use @nestjs/throttler globally, then relax specific routes. A default of 60 requests per minute per IP is a reasonable starting point for a JSON API.
ThrottlerModule.forRoot({
throttlers: [{ ttl: 60_000, limit: 60 }],
});
Return 429 with a stable body. Do not leak internal counters or Redis errors in the response.
Security headers
Helmet is enough for most NestJS services. Set it once after NestFactory.create:
app.use(helmet());
Confirm X-Content-Type-Options, Referrer-Policy, and a conservative Content-Security-Policy on any route that serves HTML. For a JSON-only API, still send X-Content-Type-Options: nosniff.
Ship this checklist before the first public client. Defaults are convenient; they are also how APIs get scraped and misused.