How HTTP Works

Request header

> Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43

Forwarded

Learn how the Forwarded header preserves original client information (IP, protocol, host) that would otherwise be lost when requests pass through proxies.

Reviewed 2 min readintermediate2 sourcesTry itMarkdown
Direction
Request
Category
Proxies
Spec
RFC 7239 §4
JS can set it
Yes
CORS-safelisted
No: may trigger a preflight
On this page

TL;DR: Forwarded is the standard header proxies use to pass along who the client was: for (the previous peer, usually the client IP), by (the proxy), host, and proto. Commas separate hops and semicolons separate parameters. Quote bracketed IPv6 addresses, and trust only the parts your own proxies added.

What is Forwarded?

RFC 7239 defines this header, and it’s still the current spec; RFC 9110 didn’t replace it. Every parameter is optional. for doesn’t have to hold a client IP either: it can be unknown or an obfuscated identifier.

Syntax

The headers and output on this page are examples.

Forwarded: for=192.0.2.60;proto=https;host=example.com
Forwarded: for="[2001:db8::1]:4711";proto=https;host="example.com:8443"

IPv6 addresses need brackets and quotes. Quote any value that contains a colon, including a hostname with a port. Parameter names are case-insensitive, and each parameter appears at most once per element.

Example

A two-hop chain:

Forwarded: for=192.0.2.60;proto=https, for=198.51.100.7;by=203.0.113.5

Each proxy that supports the header appends its own element on the right. Plenty of proxies don’t, so the list may be missing hops.

Parsing and Trust

The client can send the first element itself. A trusted proxy connection vouches only for what that proxy added, not for anything earlier in the list. Before you use Forwarded for authorization or to decide a client’s address, find out which of your proxies strip it, append to it, or regenerate it. RFC 7239 §8.1 covers these limits.

Implementation

If nginx is your IPv4 edge and receives requests straight from clients, this replaces whatever Forwarded value came in. Add it to an existing proxy location:

proxy_set_header Forwarded 'for="$remote_addr";proto=$scheme';
proxy_pass http://127.0.0.1:3000;

It sends only for and proto, and it’s IPv4-only: on an IPv6 listener you need to wrap the address in brackets before putting it in for. If TLS terminates in front of nginx, $scheme describes the inner connection, not what the browser used. See nginx proxy_set_header.

To see what reaches your app, add a diagnostic Express endpoint that echoes the raw header:

app.get('/forwarding', (req, res) => {
  res.json({ forwarded: req.get('forwarded') ?? null })
})

Then send a forged value through the nginx config above:

curl -H 'Forwarded: for=192.0.2.60;proto=https' http://localhost/forwarding

You should see nginx’s value, not the one you sent. Change the URL to match your proxy listener. This confirms nginx rewrites the header; it doesn’t test a multi-hop parser.

Forwarded vs X-Forwarded-*

Forwarded gives everyone one grammar, but frameworks won’t read it just because it exists. Express’s trust proxy documentation, for example, covers the X-Forwarded fields. Check what your app consumes before you change what your proxy sends.

Common Mistakes

Splitting the header on every comma and semicolon breaks on quoted strings, which can contain those delimiters and escapes. Use a parser that understands quoting. Another trap: preferring a client-supplied Forwarded value over a trusted proxy’s X-Forwarded value just because Forwarded is the standard name.

At each trust boundary, pick one source of truth for scheme, host, and client address. If you support both header families, decide what happens when they conflict. Neither family cryptographically proves where a request came from.

Frequently asked questions

What is the Forwarded header?

Forwarded is the RFC 7239 header that proxies use to pass along the original client address, protocol, and host. It standardizes what the X-Forwarded-* headers carry, but most frameworks still read the X-Forwarded-* versions by default.

What parameters does Forwarded support?

The defined parameters are for, by, host, and proto. They are optional; for and by may contain addresses, unknown, or obfuscated identifiers.

Should I use Forwarded or X-Forwarded-For?

Use whichever format your proxy emits and your application actually reads. If you accept both, decide which one wins when they disagree. The standard name makes the format consistent, not the contents trustworthy.

Is Forwarded secure?

No more than X-Forwarded-For. Any client can send a Forwarded header, so trust only the elements added by proxies you control, and configure your application to ignore the rest.

Sources

  1. MDN Web Docs: Forwardeddeveloper.mozilla.org
  2. RFC 7239: Forwardedrfc-editor.org

Keep going

Browse /search