{"record":{"id":"ad31b34b6ec38414","repo":"nestjs/nest","slug":"http-adapter-does-not-support-filtering-on-host","errorCode":null,"errorMessage":"HTTP adapter does not support filtering on host: \"${host}\"","messagePattern":"HTTP adapter does not support filtering on host: \"(.+?)\"","errorType":"http","errorClass":"InternalServerErrorException","httpStatus":500,"severity":"error","filePath":"packages/core/router/router-explorer.ts","lineNumber":388,"sourceCode":"    ) => {\n      (req as Record<string, any>).hosts = {};\n      const hostname = httpAdapterRef.getRequestHostname(req) || '';\n\n      for (const exp of hostRegExps) {\n        const match = hostname.match(exp.regexp);\n        if (match) {\n          if (exp.keys.length > 0) {\n            exp.keys.forEach((key, i) => (req.hosts[key.name] = match[i + 1]));\n          } else if (exp.regexp && match.groups) {\n            for (const groupName in match.groups) {\n              req.hosts[groupName] = match.groups[groupName];\n            }\n          }\n          return handler(req, res, next);\n        }\n      }\n      if (!next) {\n        throw new InternalServerErrorException(\n          unsupportedFilteringErrorMessage,\n        );\n      }\n      return next();\n    };\n  }\n\n  private applyVersionFilter<T extends HttpServer>(\n    router: T,\n    routePathMetadata: RoutePathMetadata,\n    handler: Function,\n  ) {\n    const version = this.routePathFactory.getVersion(routePathMetadata)!;\n    return router.applyVersionFilter(\n      handler,\n      version,\n      routePathMetadata.versioningOptions!,\n    );","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/nestjs/nest/blob/dd75d7bd8c5e88048587e6768d36eb695f3e7a25/packages/core/router/router-explorer.ts#L370-L406","documentation":"Host-restricted routes (`@Controller({ path: 'x', host: 'api.example.com' })`) are implemented as a wrapper that matches `req.hostname` against the configured host patterns and falls through to `next()` when nothing matches. When the underlying HTTP adapter registers the handler directly (not as middleware with a next function — the Fastify case) there is no fallback, so a request whose hostname matches none of the patterns ends in InternalServerErrorException 'HTTP adapter does not support filtering on host'.","triggerScenarios":"Using Fastify with host-based controllers and accessing the app through a hostname/IP not covered by any pattern (localhost, 127.0.0.1, container IP, another domain); a host regex that does not account for the port-bearing Host header value; moving an Express app with virtual hosts to Fastify where the middleware-style registration is unavailable; API gateways rewriting Host before the request reaches the service.","commonSituations":"Local development against localhost for a controller restricted to 'api.example.com'; Kubernetes ingress sending internal hostnames; multi-tenant SaaS routing by subdomain behind proxies; smoke tests hitting the pod IP directly.","solutions":["Add the hostnames you actually serve to the filter: `@Controller({ host: ['api.example.com', 'localhost'] })` or a parametrized pattern like `:subdomain.example.com`.","Switch to the Express adapter if full host-based (virtual host) routing is a hard requirement — it supports the middleware fallback.","Fix the Host header at the proxy/ingress level (preserve upstream Host) so the app sees the expected hostname.","As a safety net, map the resulting InternalServerError to a 404 with an exception filter for unmatched host traffic."],"exampleFix":"// before (Fastify + only prod host in filter -> curl http://localhost:3000/x => 500)\n@Controller({ path: 'reports', host: 'api.example.com' })\nexport class ReportsController {}\n\n// after\n@Controller({ path: 'reports', host: ['api.example.com', 'localhost'] })\nexport class ReportsController {}","handlingStrategy":"validation","validationCode":"// Validate Host before host-filtered routes run; respond 404 instead of a 500\nimport { Injectable, NestMiddleware } from '@nestjs/common';\nimport { Request, Response, NextFunction } from 'express';\n\nconst ALLOWED_HOSTS = ['api.example.com', 'localhost'];\n\n@Injectable()\nexport class HostAllowlistMiddleware implements NestMiddleware {\n  use(req: Request, res: Response, next: NextFunction) {\n    const hostname = (req.hostname || '').toLowerCase();\n    if (!ALLOWED_HOSTS.includes(hostname)) {\n      return res.status(404).json({ statusCode: 404, message: 'Unknown host' });\n    }\n    next();\n  }\n}\n// register with consumer.apply(HostAllowlistMiddleware).forRoutes('*') BEFORE host-filtered controllers","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Include every hostname the service is reached by (localhost, container IP, internal ingress) in host filters.","Prefer the Express adapter when host-based routing is central to the app.","Preserve the Host header in proxies/ingresses so filters match what you configured.","Cover host-filtered routes in smoke tests using the exact Host header production uses."],"tags":["routing","host-filtering","fastify","virtual-host","http-500"],"backgroundTag":"host-based-routing-unsupported","analyzedSha":"dd75d7bd8c5e88048587e6768d36eb695f3e7a25","analyzedAt":"2026-08-21T19:39:39.867Z","contentChangedAt":"2026-08-21T19:39:39.867Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}