Relevant source files
The following files were used as context for generating this wiki page:
- src/helper/ssg/ssg.ts
- src/helper/route/index.ts
- src/helper/ssg/plugins.ts
- src/hono-base.ts
- src/jsx/dom/render.ts
- src/context.ts
- src/router/reg-exp-router/prepared-router.ts
- src/adapter/deno/ssg.ts
- src/helper/ssg/utils.ts
- src/adapter/cloudflare-pages/handler.ts
- src/client/client.ts
- src/adapter/aws-lambda/handler.ts
- src/adapter/bun/ssg.ts
- src/middleware/cache/index.ts
- src/helper/ssg/middleware.ts
- src/middleware/serve-static/index.ts
- src/helper/dev/index.ts
- src/helper/ssg/index.ts
- src/middleware/jsx-renderer/index.ts
- src/preset/quick.ts
- src/adapter/lambda-edge/handler.ts
- src/router/reg-exp-router/router.ts
- src/adapter/netlify/handler.ts
- src/router/trie-router/node.ts
- src/router/linear-router/router.ts
- src/adapter/deno/serve-static.ts
- perf-measures/type-check/scripts/generate-app.ts
- src/adapter/bun/serve-static.ts
- src/router/reg-exp-router/trie.ts
- src/adapter/vercel/handler.ts
Overview
Static site generation (SSG) in Hono compiles application routes into static HTML assets and files through programmatic orchestrators like toSSG and generator functions such as fetchRoutesContent. By inspecting registered application endpoints, expanding parameter sets, and executing requests within isolated runtime contexts, Hono bridges dynamic server routing with flat-file deployment pipelines.
Sources: src/helper/ssg/ssg.ts:205-221, src/helper/ssg/ssg.ts:368-372
Generation Pipeline and Lifecycle
Execution Flow and Orchestration
The static site generation lifecycle is coordinated by the public toSSG orchestrator and the underlying generator fetchRoutesContent. When invoked with a Hono application, file system module, and optional configuration parameters, toSSG initializes hook collections and plugin pipelines, defaulting to defaultPlugin when no custom plugins are supplied.
Sources: src/helper/ssg/ssg.ts:368-372
Sources: src/helper/ssg/ssg.ts:205-334, src/helper/ssg/ssg.ts:368-397
Note
Deprecated hook options (beforeRequestHook, afterResponseHook, afterGenerateHook) on ToSSGOptions are normalized and merged into the active plugin hook arrays alongside any configured plugins items during orchestrator initialization.
Sources: src/helper/ssg/ssg.ts:185-197, src/helper/ssg/ssg.ts:372-397
Concurrency Management and Output Writing
Execution tasks across routes and requests are managed by a pool created via createPool using the concurrency option defined in ToSSGOptions. As content generators yield route payloads, saveContentToFile tracks created directories within a module-scoped createdDirs set to avoid redundant directory creation calls during file writing.
Sources: src/helper/ssg/ssg.ts:195-195, src/helper/ssg/ssg.ts:223-223, src/helper/ssg/ssg.ts:309-327
| Option / Variable | Type | Default / Behavior | Purpose |
|---|---|---|---|
concurrency | number | undefined (unrestricted) | Limits concurrent execution pools during route fetching. |
extensionMap | Record<string, string> | undefined | Maps MIME types or file extensions during output path generation. |
plugins | SSGPlugin[] | [defaultPlugin()] | Extends generation lifecycle with custom hooks. |
createdDirs | Set<string> | New Set | Caches created directory paths to optimize file writing. |
Sources: src/helper/ssg/ssg.ts:195-197, src/helper/ssg/ssg.ts:309-314, src/helper/ssg/ssg.ts:372-372
Route Filtering and Parameter Expansion
Route Inspection and Filtering
The static generation pipeline begins by examining the application's registered route table via filterStaticGenerateRoutes. This utility reduces hono.routes by inspecting each route's HTTP method and handler type, retaining only endpoints capable of serving static content. Specifically, it selects routes registered with the GET method or METHOD_NAME_ALL whose target handler is not classified as middleware.
Sources: src/helper/ssg/utils.ts:61-71
Warning
Middleware handlers are deliberately excluded from static generation output. A route whose target handler is identified as middleware via isMiddleware will be filtered out, preventing middleware-only definitions from generating empty or invalid static files.
Sources: src/helper/ssg/utils.ts:65-68
Dynamic Parameter Detection and Expansion
Once static routes are filtered, fetchRoutesContent evaluates each route path for dynamic segments using isDynamicRoute. A path is classified as dynamic if any of its slash-separated segments start with a colon (:) or contain a wildcard (*).
Sources: src/helper/ssg/ssg.ts:225-249, src/helper/ssg/utils.ts:73-75
Sources: src/helper/ssg/ssg.ts:245-269, src/helper/ssg/utils.ts:73-75
Important
If a dynamic route fails to attach ssgParams to the request object during the initial fetch execution, the route is skipped entirely and yields no content. Non-dynamic routes automatically default to an empty parameter set [{}] so that a single static file is generated.
Sources: src/helper/ssg/ssg.ts:245-251
Static Generation Middleware Controls
Context Tracking and Middleware Helpers
The SSG middleware module exposes runtime helpers that inspect or modify generation behavior depending on execution context. The experimental isSSGContext function checks whether c.env[SSG_CONTEXT] evaluates to a truthy value, identifying when a request runs within a static generation build rather than a live server. Two supplementary middleware factories, disableSSG and onlySSG, leverage this environment flag to control route inclusion: disableSSG assigns the header x-hono-disable-ssg (X_HONO_DISABLE_SSG_HEADER_KEY) and returns a 404 via c.notFound(), whereas onlySSG restricts execution exclusively to SSG contexts, returning 404 for live requests.
Sources: src/helper/ssg/middleware.ts:5-6, src/helper/ssg/middleware.ts:56-83
| Helper / Constant | Type | Purpose / Behavior |
|---|---|---|
SSG_CONTEXT | string (HONO_SSG_CONTEXT) | Environment key identifying an active static generation build. |
X_HONO_DISABLE_SSG_HEADER_KEY | string (x-hono-disable-ssg) | Response header key signalling that static generation is disabled for a route. |
isSSGContext | (c: Context) => boolean | Checks if the current context environment contains the SSG tracking key. |
disableSSG | () => MiddlewareHandler | Skips static generation by writing the disable header and returning a 404 response. |
onlySSG | () => MiddlewareHandler | Restricts handler execution to static generation contexts only. |
Sources: src/helper/ssg/middleware.ts:5-6, src/helper/ssg/middleware.ts:56-83
SSG Parameter Registration
Dynamic routes register their concrete parameter combinations through the ssgParams middleware. This function accepts either a static SSGParams array or a generator function (c: Context) => SSGParams | Promise<SSGParams>. When encountered on a dynamic route path verified by isDynamicRoute, ssgParams evaluates the parameter definitions, attaches them to c.req.raw.ssgParams (cast as AddedSSGDataRequest), and immediately aborts subsequent handler execution by returning c.notFound().
Sources: src/helper/ssg/middleware.ts:36-49
Caution
The ssgParams middleware invokes c.notFound() after attaching parameters to the raw request object. Consequently, any handler placed after ssgParams on the same route definition will be bypassed during the parameter-collection phase.
Sources: src/helper/ssg/middleware.ts:43-49
File Resolution and Output Writing
Path Calculation and Extension Mapping
export const saveContentToFile = async (
data: Promise<{ routePath: string; content: string | ArrayBuffer; mimeType: string } | undefined>,
fsModule: FileSystemModule,
outDir: string,
extensionMap?: Record<string, string>
): Promise<string | undefined> => {
const awaitedData = await data
if (!awaitedData) {
return
}
const { routePath, content, mimeType } = awaitedData
const filePath = generateFilePath(routePath, outDir, mimeType, extensionMap)
const dirPath = dirname(filePath)
if (!createdDirs.has(dirPath)) {
await fsModule.mkdir(dirPath, { recursive: true })
createdDirs.add(dirPath)
}
if (typeof content === 'string') {
await fsModule.writeFile(filePath, content)
} else if (content instanceof ArrayBuffer) {
await fsModule.writeFile(filePath, new Uint8Array(content))
}
return filePath
}Sources: src/helper/ssg/ssg.ts:310-334
Directory Traversal Safeguards
export const ensureWithinOutDir = (outDir: string, filePath: string): void => {
const normalizedOutDir = joinPaths('/', outDir)
const normalizedFilePath = joinPaths('/', filePath)
if (
normalizedFilePath !== normalizedOutDir &&
!normalizedFilePath.startsWith(`${normalizedOutDir}/`)
) {
throw new Error(`Path traversal detected: "${filePath}" is outside the output directory`)
}
}Sources: src/helper/ssg/utils.ts:77-87
Caution
The ensureWithinOutDir helper normalizes both the target output directory and the calculated file path via joinPaths, explicitly verifying that the file path matches or strictly begins with the normalized output directory prefix followed by a slash. If a route path attempts directory traversal using relative segments, an error is thrown to halt output writing.
Sources: src/helper/ssg/utils.ts:77-87
Lifecycle Hooks and Redirect Plugins
Hook Interception Points
The static site generation pipeline exposes multiple hook interception points across route execution to inspect or transform requests and responses. Before a route request is evaluated, beforeRequestHook intercepts the incoming Request object (AddedSSGDataRequest), allowing modifications or cancellation by returning a falsy value. Similarly, afterResponseHook evaluates generated Response instances. If a response carries the header defined by X_HONO_DISABLE_SSG_HEADER_KEY, generation for that route is skipped.
Sources: src/helper/ssg/ssg.ts:185-194, src/helper/ssg/ssg.ts:234-281
Warning
The defaultPlugin checks response status codes and rejects any response where res.status !== 200, meaning non-success responses will be dropped unless handled by custom plugins placed earlier in the plugin chain.
Sources: src/helper/ssg/plugins.ts:11-20
Client-Side Redirect HTML Synthesis
const REDIRECT_STATUS_CODES = new Set([301, 302, 303, 307, 308])
const generateRedirectHtml = (location: string) => {
// prettier-ignore
const content = html`<!DOCTYPE html>
<title>Redirecting to: ${location}</title>
<meta http-equiv="refresh" content="0;url=${location}" />
<meta name="robots" content="noindex" />
<link rel="canonical" href="${location}" />
<body>
<a href="${location}">Redirecting to <code>${location}</code></a>
</body>
`
return content.toString().replace(/\n/g, '')
}Sources: src/helper/ssg/plugins.ts:22-36
The redirectPlugin catches specific HTTP redirect status codes and transforms them into self-contained HTML redirection pages suitable for static hosting environments where server-side headers are absent.
| Constant Set / Function | Values / Behavior | Purpose |
|---|---|---|
REDIRECT_STATUS_CODES | 301, 302, 303, 307, 308 | Identifies HTTP response statuses eligible for static redirect page generation. |
generateRedirectHtml | HTML string with <meta http-equiv="refresh"> | Synthesizes a static HTML document containing client-side refresh tags and fallback anchor links pointing to the destination Location header. |
redirectPlugin | SSGPlugin | Intercepts redirect responses, extracts the Location header, and returns a 200 HTML Response wrapping the synthesized redirect markup. |
Sources: src/helper/ssg/plugins.ts:22-72
Tip
When using redirectPlugin alongside defaultPlugin, position redirectPlugin first in the plugins array. Because defaultPlugin rejects non-200 responses, placing defaultPlugin before redirectPlugin would drop redirect responses before they can be converted into HTML pages.
Sources: src/helper/ssg/plugins.ts:38-49
Related
On This Page
OverviewGeneration Pipeline and LifecycleExecution Flow and OrchestrationConcurrency Management and Output WritingRoute Filtering and Parameter ExpansionRoute Inspection and FilteringDynamic Parameter Detection and ExpansionStatic Generation Middleware ControlsContext Tracking and Middleware HelpersSSG Parameter RegistrationFile Resolution and Output WritingPath Calculation and Extension MappingDirectory Traversal SafeguardsLifecycle Hooks and Redirect PluginsHook Interception PointsClient-Side Redirect HTML SynthesisRelated