Node.js

HTTPS Redirect in Node.js / Express

For a Node.js/Express app, how you enforce HTTPS depends on whether your app is directly handling TLS or sitting behind a reverse proxy (Nginx, a load balancer, or a platform like Heroku) that terminates TLS for you — which is the far more common setup in production.

Behind a proxy (the common case)

app.use((req, res, next) => {
  if (req.headers['x-forwarded-proto'] !== 'https') {
    return res.redirect(301, `https://${req.headers.host}${req.url}`);
  }
  next();
});

This checks the X-Forwarded-Proto header, which the proxy sets to indicate the original protocol the visitor used — Express itself never sees the raw TLS connection in this setup, so checking req.secure directly won't work correctly unless you also set app.set('trust proxy', 1).

If Express is terminating TLS directly

Less common in production, but if your app handles the certificate itself (via https.createServer()), you'd instead run a separate plain HTTP server whose only job is redirecting every request to the HTTPS version, since the app can't listen on both plain and secure protocols on the same port.

Why trust proxy matters for Express apps behind any reverse proxy

Without explicitly calling app.set('trust proxy', 1), Express has no way to correctly interpret the X-Forwarded-Proto header a proxy sets, meaning any logic checking req.secure or relying on Express's own understanding of the connection's security will behave incorrectly when the app is deployed behind a proxy, which is the standard production deployment pattern.

What happens if you check req.secure directly instead of the forwarded header

Behind a proxy that's already terminated TLS, req.secure will read as false for every single request regardless of whether the original visitor connection was actually HTTPS, since Express only sees the plain HTTP connection arriving from the proxy — this mismatch is the most common cause of an Express app stuck in a redirect loop when deployed behind Heroku, Vercel, or any similar platform.

How to test the middleware locally before deploying it

Simulating the X-Forwarded-Proto header locally, using a tool like curl with a custom header, lets you verify your Express redirect middleware behaves correctly before deploying behind a real proxy — catching a logic error in local testing is considerably faster than debugging it in a live, deployed environment.

How this same pattern applies to other Node.js frameworks beyond Express

The underlying principle, checking X-Forwarded-Proto rather than a direct connection check, applies identically to other Node.js frameworks like Koa or Fastify, even though the specific middleware syntax differs — understanding the concept transfers directly regardless of which specific framework a given project uses.

What a health check endpoint needs to consider regarding this redirect logic

A load balancer's health check endpoint should typically bypass the HTTPS redirect entirely, since health checks often run over plain HTTP internally and would otherwise receive a redirect response instead of the expected health status — excluding the health check path specifically from your redirect middleware avoids this false-negative health check failure.

Why placing the redirect middleware early in the middleware stack matters

Positioning the HTTPS redirect check before other middleware, particularly before any middleware that processes request bodies or performs other work, ensures a request needing redirection is handled immediately rather than after unnecessary processing that will be discarded anyway.

Why understanding this pattern pays off across your broader Node.js infrastructure

Once you understand the core principle, trust the proxy's forwarded header rather than checking connection state directly, applying it correctly across every Node.js service in a broader infrastructure becomes straightforward, rather than needing to rediscover the same lesson independently for each new service.

What TypeScript-specific considerations apply to this same middleware pattern

The redirect middleware pattern translates directly into TypeScript with proper type annotations for the Express Request and Response objects — the underlying logic checking X-Forwarded-Proto remains identical, only the language syntax and type safety differ from the plain JavaScript version.

How this pattern needs adjusting for a GraphQL API versus a traditional REST endpoint

Because the redirect middleware operates at the HTTP request level before any GraphQL-specific request parsing occurs, the same middleware pattern applies identically to a GraphQL API as to a traditional REST endpoint — no GraphQL-specific adjustment is needed for the HTTPS enforcement itself.