Redirects and Middleware
Relevant source files
The following files were used as context for generating this wiki page:The routing system in this Next.js project uses a combination of built-in Next.js redirects and custom Edge Middleware to handle URL mapping, content negotiation, and legacy path support. The redirects.js file defines static redirect rules that are consumed by next.config.js for permanent URL changes. The middleware, defined in src/middleware.ts, intercepts incoming requests to documentation pages, determines if the user is requesting raw Markdown or an HTML page, and internally rewrites the request to a dynamic page handler (/dynamic/[...slug].tsx) for final processing.
This dual approach allows for both simple, permanent redirects for SEO and URL hygiene, and more complex, dynamic routing logic for serving different content formats from the same URL.
Static Redirects
The project uses the standard Next.js redirects configuration to handle permanent redirects for legacy or changed URLs. These rules are defined in a dedicated file and loaded into the main Next.js configuration.
Sources: next.config.js, redirects.js
Configuration
The next.config.js file asynchronously loads the redirect rules from redirects.js.
// File: next.config.js:14-16
async redirects() {
return redirects;
},
Sources: next.config.js:14-16
The redirects.js file exports two arrays: redirects and hashRedirects. The redirects array contains objects defining the source path, destination path, and permanent status. The hashRedirects are also defined but their implementation is not present in the analyzed Next.js configuration or middleware.
// File: redirects.js:1-7
const redirects = [
{
source: "/reference/s",
destination: "/reference/templates",
permanent: true,
},
// ...
];
// File: redirects.js:95-101
const hashRedirects = [
{
source: "#application-failed-to-respond",
destination: "/reference/errors/application-failed-to-respond",
permanent: true,
},
// ...
];
Sources: redirects.js:1-7, 95-101
Redirect Rules
The following table shows examples of redirect rules defined in the system. All are marked as permanent (308 status code).
| Source Path | Destination Path |
|---|---|
/getting-started | /quick-start |
/develop/:slug* | /guides/:slug* |
/deploy/exposing-your-app | /guides/public-networking |
/databases/:slug* | /guides/:slug* |
/reference/pricing | /reference/pricing/plans |
Sources: redirects.js:8-12, 13-17, 18-22, 43-47, 85-89
Middleware for Content Negotiation
A Next.js Edge middleware is used to intercept requests, validate them against existing documentation pages, and rewrite them to a dynamic route for rendering. Its primary function is to enable content negotiation, allowing clients to request either a standard HTML page or the raw Markdown source.
Sources: src/middleware.ts
Middleware Logic Flow
The diagram below illustrates the decision-making process within the middleware for each incoming request.
This diagram shows how the middleware handles requests, either passing them through, rewriting them for HTML rendering, or rewriting them to serve raw Markdown.
Sources: src/middleware.ts:18-39
Implementation Details
-
Page URL Set: The middleware first creates a
Setof all valid page URLs fromallPages, which is generated by Contentlayer. This is used for efficient path validation.// File: src/middleware.ts:5 const pageUrlSet = new Set(allPages.map(page => page.url)); -
Matcher: The middleware is configured to run on all paths in the project.
// File: src/middleware.ts:41-43 export const config = { matcher: "/:path*", };Sources: src/middleware.ts:41-43
-
Content Negotiation: The middleware determines if a Markdown version of the page is being requested based on three conditions:
- The path ends with a
.mdextension. - The URL has a
format=mdquery parameter. - The
AcceptHTTP header preferstext/markdownortext/plainovertext/html.
// File: src/middleware.ts:7-12 function prefersMarkdown(acceptHeader: string): boolean { const types = acceptHeader.split(","); const mdIdx = types.findIndex(t => t.includes("text/markdown") || t.includes("text/plain")); const htmlIdx = types.findIndex(t => t.includes("text/html")); return mdIdx !== -1 && (htmlIdx === -1 || mdIdx < htmlIdx); }// File: src/middleware.ts:31-34 const wantsMarkdown = hasMdExtension || searchParams.get("format") === "md" || prefersMarkdown(request.headers.get("accept") || "");Sources: src/middleware.ts:7-12, 31-34
- The path ends with a
-
URL Rewrite: If the path is a valid documentation page, the middleware rewrites the request to
/dynamic/[...slug]. If Markdown is requested, it addsformat=mdto the query string.// File: src/middleware.ts:36-40 const url = request.nextUrl.clone(); url.pathname = "/dynamic" + pagePath; if (wantsMarkdown) { url.searchParams.set("format", "md"); } return NextResponse.rewrite(url);Sources: src/middleware.ts:36-40
Dynamic Page Rendering
The src/pages/dynamic/[...slug].tsx page is the destination for all requests rewritten by the middleware. It uses Server-Side Rendering (SSR) to handle both HTML page generation and raw Markdown delivery.
Sources: src/pages/dynamic/[...slug].tsx
Request Handling Sequence
The following sequence diagram shows the complete interaction from a user request to the final response, covering both HTML and Markdown cases.
Server-Side Logic (getServerSideProps)
The getServerSideProps function in [...slug].tsx contains the logic to serve different content types.
- It finds the page data based on the slug.
- If
context.query.format === "md", it constructs the full Markdown content (including frontmatter), sets theContent-Typeheader totext/markdown, and writes the content directly to the response, bypassing the React rendering process. - If
formatis notmd, it returns the page data as props to thePostPagecomponent, which then renders the MDX content as a standard HTML page.
// File: src/pages/dynamic/[...slug].tsx:66-74
// Return raw markdown if format=md
if (context.query.format === "md") {
const markdown = reconstructMarkdownWithFrontmatter(
{ title: page.title, description: page.description, url: page.url },
page.body.raw
);
context.res.setHeader("Content-Type", "text/markdown; charset=utf-8");
context.res.write(markdown);
context.res.end();
return { props: {} };
}
Sources: src/pages/dynamic/[...slug].tsx:66-74
Summary
The combination of Next.js redirects and custom middleware provides a powerful and flexible routing layer for the documentation site. Static redirects ensure that old links continue to work and that URLs remain canonical. The middleware enables advanced features like content negotiation, allowing the same URL to serve both human-readable HTML and machine-readable Markdown, which is particularly useful for integrations and developer tools that might consume the documentation programmatically. This entire system is orchestrated to direct traffic to a single dynamic server-side rendered page that handles the final content delivery.