Skip to content
Built with

Rewrites ​

SDK Reference

A rewrite serves a request from a different internal path without changing the URL in the browser. c.rewrite(), c.originalUrl() and c.isRewritten() are available on every Hono Context the SDK gives you. See the Rewrites guide for the overview.

On this pageRewrite a request inside a handler · Read the original URL · Check whether a request was rewritten

c.rewrite(destination) ​

Sends the current request to destination and returns the resulting Response. The browser URL does not change. This is a server-side hop.

If destination has no query string, the original query is kept. If it has one, it replaces the original query. destination must start with /. Known routes autocomplete through RewriteDestination. Any other string still works for dynamic paths.

c.redirect() also keeps query params for internal destinations, but it merges them. On a clash, the destination's params win. Params only in the request are appended.

ts
import { defineHandler } from 'void';

export const GET = defineHandler((c) => {
  const locale = c.req.header('accept-language')?.startsWith('de') ? 'de' : 'en';
  return c.rewrite(`/${locale}${new URL(c.req.url).pathname}`);
});

Signature:

ts
interface CloudContext {
  rewrite(destination: RewriteDestination): Promise<Response>;
}

Caveats:

  • destination must start with a single / and point to an internal path. External URLs, protocol-relative paths (//host/...) and relative paths throw at once. Use fetch() for cross-origin calls.
  • A destination that ends in a known static-asset extension, such as /hero.png, /app.css or /data.json, throws VoidAssetRewriteError before the hop. This error is exported from "void". Runtime rewrites cannot reach assets. Use a static routing.rewrites rule or a _redirects 200! entry. .html is not blocked, because .html paths can be real route handlers. See Rewrites.
  • Middleware runs again on every rewrite hop. Guard side effects with c.isRewritten().
  • Client-side navigation with Link does not trigger server rewrites. Only full HTTP requests do.
  • Purge the destination path to clear rewritten ISR entries.

c.originalUrl() / c.isRewritten() ​

Context methods that show the rewrite state. c.originalUrl() returns the URL before the rewrite as a URL object. On the first hop it returns null. c.isRewritten() returns true on any rewritten context and false on the original. They work for static and middleware rewrites.

ts
import { defineMiddleware } from 'void';

export default defineMiddleware(async (c, next) => {
  if (!c.isRewritten()) {
    // Only log the user-visible request, not internal rewrite hops.
    console.log(`${c.req.method} ${c.req.path}`);
  }
  await next();
});

Signature:

ts
interface CloudContext {
  originalUrl(): URL | null;
  isRewritten(): boolean;
}

RewriteDestination ​

ts
import type { RewriteDestination } from 'void/routes';

Suggests known route patterns in your editor and still accepts dynamic strings. It is used as:

  • The parameter type of c.rewrite().
  • The type of destination entries in routing.rewrites and routing.fallbacks in void.config.ts.

Like RouteMap, RewriteDestination lives in the virtual void/routes module. It refreshes whenever routes change.

Next steps ​

implemented.stream AI