[ HTTP & responses ]
@sdxc/no-www-middleware
Router middleware that permanently redirects a www. hostname to the apex domain
- Depends on
remix- Used by
- blog
Router middleware that permanently redirects a www. hostname to the apex domain, so a site answers on one host for search engines and for the cookies scoped to it.
Installation
npm add @sdxc/no-www-middleware
It goes on a remix/router middleware chain, so remix (v3) installs alongside it.
Usage
Redirect To The Apex Domain
import { noWWW } from "@sdxc/no-www-middleware";
import { createRouter } from "remix/router";
let router = createRouter({ middleware: [noWWW()] });
// GET https://www.example.com/posts?page=2 -> 308, Location: https://example.com/posts?page=2
// GET https://example.com/posts -> the matched handler
// GET https://blog.example.com/ -> the matched handler
The Longhand
noWWW() stands in for this hand-written middleware:
import type { Middleware } from "remix/router";
let stripWWW: Middleware = (context, next) => {
let url = new URL(context.request.url);
if (!url.hostname.startsWith("www.")) return next();
url.hostname = url.hostname.slice(4);
return new Response(null, { status: 308, headers: { Location: url.href } });
};
API
noWWW(): Middleware
Returns a middleware for a router's, controller's, or route's middleware chain. A request whose hostname starts with www. gets a 308 whose Location is the request URL with that leading label removed, so scheme, port, path and query string carry over. Every other request passes to next() untouched.
Only the first label is checked:
www.example.comredirects, whileblog.www.example.comandwwwexample.compass through.Only one label is removed, so
www.www.example.comredirects towww.example.com, which redirects again.The hostname is compared as the URL parser normalizes it, so
WWW.Example.comredirects toexample.com.A
308makes the client repeat the method and body, so aPOSTtowww.example.com/commentsarrives atexample.com/commentsas aPOST.
Pattern: Placing It First
Install it near the top of the router's chain, ahead of sessions, authentication and body parsing, so a redirected request skips work its retry on the apex domain runs anyway. A middleware above it, such as a logger, still sees the redirect.
import { noWWW } from "@sdxc/no-www-middleware";
import { trailingSlash } from "@sdxc/trailing-slash-middleware";
import { createRouter } from "remix/router";
let router = createRouter({ middleware: [noWWW(), trailingSlash()] });
router.get("/posts", () => new Response("posts"));
The www. host must still reach the Worker for the redirect to run: route it to the same Worker (or point its DNS record at it) alongside the apex domain. Browsers cache a 308, so keep the apex domain as the canonical host once it is live.