new Hono(options) accepts exactly three options, defined as HonoOptions in src/hono-base.ts. There is no config file, no environment variable, no plugin registry -- all other behavior is set through methods (onError, notFound, use) or the choice of entry point (hono, hono/quick, hono/tiny).

src/hono-base.ts
export type HonoOptions<E extends Env> = {
  /**
   * `strict` option specifies whether to distinguish whether the last path is a directory or not.
   * @default true
   */
  strict?: boolean
  /**
   * `router` option specifies which router to use.
   */
  router?: Router<[H, RouterRoute]>
  /**
   * `getPath` can handle the host header value.
   */
  getPath?: GetPath<E>
}

strict (default: true)

Controls whether /hello and /hello/ are different routes. Implementation-wise it is nothing more than a choice of path extractor in the constructor:

src/hono-base.ts
this.getPath = (strict ?? true) ? (options.getPath ?? getPath) : getPathNoStrict

getPathNoStrict strips one trailing slash before matching:

src/utils/url.ts
export const getPathNoStrict = (request: Request): string => {
  const result = getPath(request)

  // if strict routing is false => `/hello/hey/` and `/hello/hey` are treated the same
  return result.length > 1 && result.at(-1) === '/' ? result.slice(0, -1) : result
}

The pinned behavior, from the test suite:

src/hono.test.ts
describe('strict is false', () => {
  const app = new Hono({ strict: false })

  app.get('/hello', (c) => {
    return c.text('/hello')
  })

  it('/hello and /hello/ are treated as the same', async () => {
    let res = await app.request('http://localhost/hello')
    // …status 200
    res = await app.request('http://localhost/hello/')
    // …status 200
  })
})

With the default strict: true, the same suite asserts /hello/ returns 404 when only /hello is registered (and vice versa). Note that strict: false normalizes the request path only -- register routes without trailing slashes.

router

Replaces the default SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] }) with any object implementing the two-method Router interface. Pinned by test:

src/hono.test.ts
it('Should be RegExpRouter', () => {
  const app = new Hono({
    router: new RegExpRouter(),
  })
  expect(app.router instanceof RegExpRouter).toBe(true)
})

Use cases and trade-offs per router are in Routing. Remember the shared constraint: SmartRouter-based apps cannot register routes after the first request has been matched.

getPath

Replaces path extraction entirely, receiving the raw Request (and { env }). The JSDoc's own example enables host-based routing by prefixing the host header onto the matched path:

src/hono-base.ts
 * const app = new Hono({
 *  getPath: (req) =>
 *   '/' + req.headers.get('host') + req.url.replace(/^https?:\/\/[^/]+(\/[^?]*)/, '$1'),
 * })
 *
 * app.get('/www1.example.com/hello', () => c.text('hello www1'))

If you pass both strict: false and a custom getPath, the custom getPath is ignored in favor of getPathNoStrict -- visible in the constructor line quoted above, and pinned by the strict is false with getPath option case in src/hono.test.ts, which passes getPath explicitly and still observes non-strict matching.

Not configuration, but adjacent

  • app.basePath('/api') -- prefixes all subsequently registered routes; returns a clone (see Composing apps).
  • app.onError(handler) / app.notFound(handler) -- replace the default 500/404 responders (see Errors and not-found).
  • strict interacts only with matching; it never rewrites the URL a handler sees via c.req.path.

Sources: src/hono-base.ts, src/utils/url.ts, src/hono.test.ts · last synced 2026-08-10 · 26de731 · version 4.13.1