HTTPS Redirect
Deserve has no HTTPS redirect middleware, and forcing one inside the app is the wrong layer for the job. TLS is terminated at the edge, so the application rarely sees the original scheme to begin with.
Why It Is Not Built In
A forced HTTPS redirect sends any http:// request back as https://. The trouble is where TLS actually lives. In production the certificate sits on a proxy or load balancer like Cloudflare or nginx, which terminates TLS and forwards plain HTTP to the origin. The app then sees http, even though the client connected over https, so a redirect built from the local scheme would bounce a request that was already secure.
That is how the redirect loop happens. The proxy speaks HTTPS to the client, the origin sees HTTP and redirects to HTTPS, the proxy forwards HTTP again, and the loop never ends. The safe place to enforce HTTPS is the layer that owns the certificate, in line with build on the platform.
Where HTTPS Belongs
The redirect itself is one line of proxy config, not application code:
- At the proxy - Cloudflare and nginx redirect
httptohttpsbefore traffic reaches the origin, so the app only ever serves the secure request. - In the browser - the
Strict-Transport-Securityheader tells a browser to use HTTPS on its own for future visits, which removes the redirect after the first secure response.
Deserve already covers the second half. The security headers middleware sets HSTS through the strictTransportSecurity option, off by default and meant to turn on once the server is reached over HTTPS.
// Tell browsers to stick to HTTPS
router.use(
Mware.securityHeaders({
strictTransportSecurity: 'max-age=31536000; includeSubDomains'
})
)
await router.serve(8000)Reading the Real Scheme
When the app does need to know whether the client used HTTPS, the answer rides in a forwarded header the proxy sets, not in the local connection. A trusted proxy adds X-Forwarded-Proto, read through ctx.header.
export function GET(ctx: Context): Response {
// Scheme the client actually used
const proto = ctx.header('x-forwarded-proto') ?? 'http'
return ctx.send.json({ secure: proto === 'https' })
}Trust this header only behind a proxy configured through trustProxy, the same trust boundary ctx.ip relies on. An untrusted client can set any header, so the value means nothing without that boundary.
Serving HTTPS Directly
A server with no proxy in front can terminate TLS itself by passing a certificate and key to Deno.serve, the runtime under server configuration. Even then the redirect from http to https is a separate listener concern, not a middleware, so the application stays focused on the request it receives.