HTTP

Header

X-Forwarded-Proto

Learn how the X-Forwarded-Proto header identifies the original protocol (HTTP/HTTPS) used by clients connecting through proxies or load balancers.

2 min read intermediate Try in Playground

TL;DR: X-Forwarded-Proto tells your app whether the user originally arrived over HTTP or HTTPS before a proxy stripped TLS and forwarded the request internally.

Why This Header Exists

Many deployments terminate TLS at the edge:

Browser --HTTPS--> Load balancer --HTTP--> App server

From the app server’s perspective, the incoming connection is plain HTTP even though the user really connected over HTTPS. X-Forwarded-Proto exists to preserve that original fact.

Typical value:

X-Forwarded-Proto: https

The Real Problem It Solves

If your app does not know the original scheme, it can make bad decisions:

  • redirect HTTPS traffic back to HTTPS again and create loops
  • fail to mark cookies as secure
  • generate http:// absolute URLs in emails or redirects
  • misreport whether a request was secure in logs or middleware

That is why this header matters even though it looks trivial.

The Classic Redirect Loop

This is the bug many teams hit first:

  1. user visits https://app.example.com
  2. load balancer terminates TLS
  3. app receives plain HTTP from the load balancer
  4. app thinks the request is insecure and redirects to HTTPS
  5. repeat forever

Once the app trusts X-Forwarded-Proto: https, it can tell the difference between internal transport and the user-facing connection.

Framework Handling

In Express, the usual fix is to trust the proxy so req.secure reflects X-Forwarded-Proto:

app.set('trust proxy', 1)

app.use((req, res, next) => {
  if (!req.secure) {
    return res.redirect(301, `https://${req.headers.host}${req.originalUrl}`)
  }
  next()
})

Other frameworks have equivalent settings. The important part is not the header lookup itself. The important part is declaring which proxy layers are allowed to speak for the original request.

Security Rule: Trust Boundaries Matter

Clients can forge X-Forwarded-Proto if they can reach your origin directly. That is why this header is only safe when:

  • the origin is behind a trusted proxy or load balancer
  • the proxy overwrites the header instead of passing through user input
  • the app only trusts forwarded headers from known internal hops

If any of those are missing, treat the header as untrusted input.

Standard Alternative

The standard Forwarded header can express the same idea with proto=https, but X-Forwarded-Proto is still the more common field in real deployments.

Frequently Asked Questions

What is X-Forwarded-Proto?

X-Forwarded-Proto tells an origin app whether the original client connected to the edge over HTTP or HTTPS before a proxy forwarded the request inward.

Why is X-Forwarded-Proto important?

Without it, an app behind TLS termination may think every request is plain HTTP. That affects redirects, secure cookies, absolute URL generation, and security behavior.

How do I use X-Forwarded-Proto?

Use it only through trusted proxy configuration. Most frameworks can translate it into a secure-request signal once you declare which proxies to trust.

Can X-Forwarded-Proto be spoofed?

Yes. Never trust it from arbitrary clients. Only trust it when your proxy overwrites it and your app accepts it only from known proxy layers.

Keep Learning