RFC 7239
Forwarded HTTP Extension
Forwarded HTTP Extension β¨
Quick title-block tea π« This is RFC 7239, called Forwarded HTTP Extension. It came from the IETF (Internet Engineering Task Force) and was written by A. Petersson and M. Nilsson at Opera Software. It's on the Standards Track (ISSN 2070-1721), dated June 2014. The header block doesn't list anything it obsoletes or updates.
Abstract β¨
Okay so, this doc makes a new HTTP header field π¦ that lets proxies spill info that would otherwise get lost on the way through. Think the originating IP address of a request, or the IP address of the proxy on the side facing the user agent. When you've got a chain of proxies, every component down the line can see all the IP addresses used in that chain of proxied HTTP requests. Iconic, right? π
It also gives proxy admins some guidelines for anonymizing where a request came from, for the privacy-minded folks π«Ά
Status of This Memo π
This is an Internet Standards Track document π
It's a product of the Internet Engineering Task Force (IETF), so it represents the consensus of the IETF community. It went through public review and got approved for publication by the Internet Engineering Steering Group (IESG). If you want more on Internet Standards, check out Section 2 of RFC 5741.
If you want the current status, any errata, or a way to send feedback, head to http://www.rfc-editor.org/info/rfc7239 πΈ
Copyright Notice βοΈ
Copyright (c) 2014 IETF Trust and the persons identified as the document authors. All rights reserved.
This doc is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents, which you can find at http://trustee.ietf.org/license-info (as they stood on the publication date). Please actually read those, bestie, because they spell out your rights and restrictions for this document. Any Code Components pulled out of this doc have to include the Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come with no warranty, as the Simplified BSD License describes π
Table of Contents ποΈ
1. Introduction ....................................................3
2. Notational Conventions ..........................................4
3. Syntax Notations ................................................4
4. Forwarded HTTP Header Field .....................................4
5. Parameters ......................................................6
5.1. Forwarded By ...............................................6
5.2. Forwarded For ..............................................6
5.3. Forwarded Host .............................................7
5.4. Forwarded Proto ............................................7
5.5. Extensions .................................................7
6. Node Identifiers ................................................8
6.1. IPv4 and IPv6 Identifiers ..................................9
6.2. The "unknown" Identifier ...................................9
6.3. Obfuscated Identifier ......................................9
7. Implementation Considerations ..................................10
7.1. HTTP Lists ................................................10
7.2. Header Field Preservation .................................10
7.3. Relation to Via ...........................................10
7.4. Transition ................................................11
7.5. Example Usage .............................................11
8. Security Considerations ........................................12
8.1. Header Validity and Integrity .............................12
8.2. Information Leak ..........................................12
8.3. Privacy Considerations ....................................12
9. IANA Considerations ............................................14
10. References ....................................................14
10.1. Normative References .....................................14
10.2. Informative References ...................................15
Appendix A. Acknowledgments .......................................16
1. Introduction β¨
Let's talk about the HTTP landscape right now. There are a ton of apps acting as proxies for user agents, and a lot of the time nobody using them even knows they're there π Maybe the proxy is just part of the infrastructure of the org running the web server. It might be doing load balancing or crypto offload. Or the proxy is inside the same org as the user and it's caching resources. Either way, these proxies make requests look like they came from the proxy's IP address, and they might change other info in the original request too. That's a loss of information from the original request π
That loss can cause real problems for a web server that actually needs the client's IP address. Using the proxy's address or other changed info just won't cut it for that server. The main uses for this info are diagnostics, access control, and abuse management. Diagnostics covers event logging, troubleshooting, and stats gathering. That info is usually only kept for short periods and only pulled up when there's a specific problem or a complaint from the client. Access control usually means keeping a list of client IP addresses that are allowed in, but that falls apart once a proxy is involved, unless the proxy is trusted and is itself set up with a list of allowed client addresses for the server. And when it comes to abuse, you need to identify the abuser, which uses a lot of the same features as diagnostics π«
Most of the time, losing that info isn't the whole point of using a proxy. It's just a side effect nobody wanted. So to get the functionality back when a proxy is in use, we need a way to disclose the original info at the HTTP level π Obviously though, if the whole reason someone uses a proxy is to stay anonymous, then that proxy won't use the feature in this document. No judgment, just facts π§
Reverse proxies hide info too. Same deal: if losing that info wasn't the goal of using the reverse proxy, it's worth finding a way to encode the info inside the HTTP messages so the consumer can actually see it π
A common way people do this right now is with non-standard header fields like X-Forwarded-For, X-Forwarded-By, and X-Forwarded-Proto. Standardizing stuff people keep wanting is a huge win, mostly because it means implementations can actually talk to each other π This doc standardizes a header field called Forwarded and gives its syntax and semantics for disclosing that info. Forwarded also keeps all the info in one single header field, which makes it way easier to correlate. With this format, you can tell what info belongs together, as long as the proxies are trusted. You just can't make those conclusions with the X-Forwarded family π
ββοΈ The Forwarded header field is optional, so proxies meant to protect privacy aren't required to operate or implement it. Their choice, no pressure πΈ
Heads up: similar issues to the ones with proxies also show up with NATs. There's more on that in [RFC6269] π¦
2. Notational Conventions π
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119].
3. Syntax Notations π§©
This spec uses the Augmented Backus-Naur Form (ABNF) notation of [RFC5234], along with the list rule extension defined in Section 7 of [RFC7230] π
4. Forwarded HTTP Header Field π
The Forwarded HTTP header field is an OPTIONAL header field. When it's used, it holds a list of parameter-identifier pairs that disclose info that gets altered or lost when a proxy is in the request path. The data in this header is sensitive (see Sections 8.2 and 8.3), so this header field should be turned off by default, and each parameter should be configured individually π Forwarded is only for HTTP requests and is not to be used in HTTP responses. This goes for forwarding proxies and reverse proxies alike. The info can be things like the source IP address of the request, the IP address of the incoming interface on the proxy, or whether HTTP or HTTPS was used. If the request goes through several proxies, each one can add a set of parameters. A proxy can also remove previously added Forwarded header fields π«Ά
The top-level list is a list of HTTP header field-values, as defined in Section 3.2 of [RFC7230]. The first element holds info added by the first proxy that implements and uses this header field, and each element after that holds info added by the next proxy in line π Since this header field is optional, any proxy in the chain may choose not to update it. Each field-value is a semicolon-separated list, and that sublist is made of parameter-identifier pairs. Those pairs are joined by an equals sign. Each parameter MUST NOT occur more than once per field-value. Parameter names are case-insensitive. Here's the ABNF syntax for the header field value:
Forwarded = 1#forwarded-element
forwarded-element =
[ forwarded-pair ] *( ";" [ forwarded-pair ] )
forwarded-pair = token "=" value
value = token / quoted-string
token = <Defined in [RFC7230], Section 3.2.6>
quoted-string = <Defined in [RFC7230], Section 3.2.6>
Examples:
Forwarded: for="_gazonk"
Forwarded: For="[2001:db8:cafe::17]:4711"
Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
Forwarded: for=192.0.2.43, for=198.51.100.17
Quick note: : and [] aren't valid characters in token, so IPv6 addresses get written as quoted-string π€
A proxy that wants to add a new Forwarded header field value has two options. It can append the value to the last existing Forwarded header field after a comma separator, or it can add a new field at the end of the header block. A proxy MAY remove all Forwarded header fields from a request. It MUST, however, make sure the correct header field gets updated when there are multiple Forwarded header fields. No messing that up, periodt π
5. Parameters π¦
This doc specifies a few parameters and the valid values for each one:
"by"identifies the user-agent facing interface of the proxy."for"identifies the node making the request to the proxy."host"is the Host request header field as received by the proxy."proto"indicates what protocol was used to make the request.
Part 1 of 3 is done, bestie π«Ά Next up is Section 5's subsections (5.1 through 5.5) and then Section 6, Node Identifiers, which is where things get extra spicy π
5.1. Forwarded By π
Okay so the "by" parameter tells you which interface the request came in through at the proxy πͺ. When proxies choose to use it, the default configuration SHOULD contain an obfuscated identifier, like the one in Section 6.3 β¨. If the server getting the proxied requests needs some address-based functionality, this parameter MAY hold an IP address instead (and maybe a port number too). The third option is the "unknown" identifier from Section 6.2 π«Ά.
After any quoted-string unescaping, a "by" value has to match the "node" ABNF from Section 6 π .
This is mainly added by reverse proxies that want to pass the info along to the backend server π. It's also lowkey useful in a multihomed setup, since it tells the backend servers which interface the request came from πΈ.
5.2. Forwarded For π
The "for" parameter discloses info about the client that started the request, plus the proxies in the chain after it π§. When proxies use it, the default configuration SHOULD contain an obfuscated identifier (see Section 6.3). If the server getting proxied requests needs address-based functionality, this parameter MAY contain an IP address instead, and possibly a port number π¦. Third option again: the "unknown" identifier from Section 6.2.
After any quoted-string unescaping, the "for" value conforms to the "node" ABNF from Section 6 β¨.
In a proxy chain where this is fully used, the first "for" parameter reveals the client where the request was first made, and then any later proxy identifiers follow in order. The last proxy in the chain is NOT in the list of "for" parameters π. Its IP address (and optionally a port number) is still easy to get, since it shows up as the remote IP address at the transport layer π. It can actually be more relevant to look at the "by" parameter of the previous "Forwarded" header field to learn about that last proxy, if one is there π.
5.3. Forwarded Host π
The "host" parameter forwards the original value of the "Host" header field π . This can be handy when, say, the origin server gets a reverse proxy that rewrites "Host" to some internal host name π₯.
After any quoted-string unescaping, a "host" value MUST conform to the Host ABNF described in Section 5.4 of [RFC7230] π«Ά.
5.4. Forwarded Proto π¦
The "proto" parameter holds the protocol type that was used πΈ. After any quoted-string unescaping, a "proto" value MUST conform to the URI scheme name defined in Section 3.1 in [RFC3986] and registered with IANA according to [RFC4395] β¨. Typical values are "http" or "https" π.
Here's a lovely example: a reverse proxy is also acting as a crypto offloader π. The origin server can then rewrite the URLs in a document to match the connection type the user agent asked for, even though every connection to the origin server is plain, unencrypted HTTP π. Iconic plumbing, honestly π.
5.5. Extensions π§
Extensions let you add extra parameters and values β¨. They're especially useful in reverse proxy environments π. All extension parameters SHOULD be registered in the "HTTP Forwarded Parameter" registry π. If an extension is expected to get widespread deployment, it SHOULD also be standardized π. More on this in Section 9 π«Ά.
6. Node Identifiers β¨
The node identifier is one of the following π:
- The client's IP address, with an optional port number πΈ
- A token indicating that the IP address of the client is not known to the proxy server π
- A generated token, allowing for tracing and debugging, while allowing the internal structure or sensitive information to be hidden π
The node identifier is defined by this ABNF syntax π :
node = nodename [ ":" node-port ]
nodename = IPv4address / "[" IPv6address "]" /
"unknown" / obfnode
IPv4address = <Defined in [RFC3986], Section 3.2.2>
IPv6address = <Defined in [RFC3986], Section 3.2.2>
obfnode = "_" 1*( ALPHA / DIGIT / "." / "_" / "-")
node-port = port / obfport
port = 1*5DIGIT
obfport = "_" 1*(ALPHA / DIGIT / "." / "_" / "-")
DIGIT = <Defined in [RFC5234], Section 3.4>
ALPHA = <Defined in [RFC5234], Section B.1>
Each identifier can optionally carry a port identifier too, which is useful for pinpointing an endpoint in a NATed environment π¦. The "node-port" can be given either as a port number or as a generated token that hides the real port number β¨. An obfuscated port is handy when the proxy owner wants to trace requests (for debugging, say) but doesn't want to reveal internal info π.
Heads up: the ABNF above also allows port numbers to be tacked onto the "unknown" identifier π. What that means is up to whoever runs the proxy adding that value to the header field π€·ββοΈ. To tell an "obfport" apart from a port, the "obfport" MUST have a leading underscore π«Ά. It also MUST consist of only "ALPHA", "DIGIT", and the characters ".", "_", and "-" π .
Also super important: an IPv6 address, and any nodename with a node-port specified, MUST be quoted, because ":" is not an allowed character in "token" π¨.
Examples:
"192.0.2.43:47011"
"[2001:db8:cafe::17]:47011"
6.1. IPv4 and IPv6 Identifiers πΈ
The ABNF rules for "IPv6address" and "IPv4address" are defined in [RFC3986] π. The "IPv6address" SHOULD follow the textual representation recommendations in [RFC5952] (so think lowercase, and compressed zeros) π.
Note that the IP address might be one from the internal nets defined in [RFC1918] and [RFC4193] π . Also, an IPv6 address is always wrapped in square brackets π.
6.2. The "unknown" Identifier π
The "unknown" identifier shows up when the identity of the preceding entity isn't known, but the proxy still wants to signal that the request got forwarded π΅οΈββοΈ. One example is a proxy server process generating an outgoing request without direct access to the incoming request TCP socket π§.
6.3. Obfuscated Identifier π
You can use a generated identifier when you want to keep internal IP addresses secret, but still let the "Forwarded" header field be used for tracing and debugging π¦. This is also handy if the proxy uses interface labels and you'd rather pass those than an IP address πΈ. Unless static assignment of identifiers is needed for the server to work, obfuscated identifiers SHOULD be randomly generated for each request π². If the server needs identifiers to persist across requests, they SHOULD NOT persist any longer than client IP addresses do β³. To tell an obfuscated identifier apart from other identifiers, it MUST have a leading underscore "_" π«Ά. It also MUST consist of only "ALPHA", "DIGIT", and the characters ".", "_", and "-" π .
Example:
Forwarded: for=_hidden, for=_SEVKISEK
7. Implementation Considerations π
7.1. HTTP Lists π
Heads up: an HTTP list lets white space sit between the identifiers, and the list can be split across multiple header fields π§Ύ. For example, this header field:
Forwarded: for=192.0.2.43,for="[2001:db8:cafe::17]",for=unknown
is equivalent to this one:
Forwarded: for=192.0.2.43, for="[2001:db8:cafe::17]", for=unknown
which is in turn equivalent to these header fields:
Forwarded: for=192.0.2.43
Forwarded: for="[2001:db8:cafe::17]", for=unknown
Same info, three outfits πβ¨.
7.2. Header Field Preservation π·
Sometimes this header field should be kept, and sometimes it shouldn't π€. A directly forwarded request should preserve it and maybe extend it π¦. If one incoming request makes the proxy send multiple outbound requests, you need to be extra careful about whether to keep the header field π§. In lots of cases it should be kept, but if an outbound request isn't a direct consequence of the incoming request, it shouldn't be kept π ββοΈ. Also consider the case where a proxy has spotted a content mismatch in a 304 response and is following the instructions in [RFC7232], Section 4.1 to repeat the request unconditionally. The new request is still basically a direct consequence of the original one, so the header field should probably be kept π.
7.3. Relation to Via π
The "Via" header field (see [RFC7230], Section 5.7.1) has a similar use case to this one π―ββοΈ. The catch is that "Via" only tells you about the proxy itself, so it leaves out info about the client connecting to the proxy server π. "Forwarded", on the other hand, is mainly about relaying info from the client-facing side of the proxy server π«Ά. And since "Via" is already widely deployed, its format can't be changed to fix the problems "Forwarded" is solving π.
It's not possible to combine info from this header field with info from the Via header field π«. Some proxies won't update "Forwarded", some won't update Via, and some update both π€·ββοΈ.
7.4. Transition π¦
If a proxy gets incoming requests with X-Forwarded-* header fields, it's encouraged to convert them into the header field described in this document, as long as it can be done sensibly β¨. If the request only has one type, like X-Forwarded-For, you can translate it to "Forwarded" by prepending each element with "for=" πΈ. Note that IPv6 addresses may not be quoted in X-Forwarded-For and may not be wrapped in square brackets, but in "Forwarded" they are both quoted and wrapped in square brackets π.
X-Forwarded-For: 192.0.2.43, 2001:db8:cafe::17
becomes:
Forwarded: for=192.0.2.43, for="[2001:db8:cafe::17]"
BUT, and this is a big but π¨, special care must be taken if, for example, both X-Forwarded-For and X-Forwarded-By exist. In that case, a conversion might not be possible, since there's no way to know which order the existing fields were added in π€―. Also, removing the X-Forwarded-For header field may cause issues for parties that haven't implemented support for this new header field yet π.
7.5. Example Usage β¨
okay so picture this bestie π
a client at IP address 192.0.2.43 sends a request through a proxy at 198.51.100.17, which passes it along to another proxy at 203.0.113.60, and only then does it reach the origin server π«Ά it's giving office worker behind a corporate malware filter, talking to a server through a reverse proxy πΈ
- The HTTP request between the client and the first proxy has no
Forwardedheader field. π - The HTTP request between the first and second proxy has a
Forwarded: for=192.0.2.43header field. π¦ - The HTTP request between the second proxy and the origin server has a
Forwarded: for=192.0.2.43,for=198.51.100.17;by=203.0.113.60;proto=http;host=example.comheader field. π
ngl this is the whole point π the header keeps growing as the request hops along, so by the time it reaches the server you can see the full trail π§
the info doesn't always get updated at every hop, and that's okay! π it could be because a node doesn't support this extension yet, or because someone decided not to disclose info about that part of the network π
8. Security Considerations π
8.1. Header Validity and Integrity
the Forwarded header field CANNOT be trusted to be correct bestie π every node on the way to the server can change it, whether by accident or on purpose, and that includes the client who made the request π¬
one way people try to make sure the header is correct is by checking that proxies are legit and then whitelisting them as trusted β¨ but this approach has at least two problems π first, the chain of IP addresses that got listed before the request reached the proxy can't be trusted. second, unless the connection between the proxies and the endpoint is secured, an attacker on the network can just modify the data π
8.2. Information Leak
the Forwarded header field can reveal the internal layout of a network behind a NAT or proxy setup, and that's often not something you want out there π you can deal with this by using obfuscated elements, by stopping internal nodes from updating the header field, or by having an egress proxy remove entries that reveal internal network info π‘οΈ
this header field should never be copied into response messages by origin servers or intermediaries π± why? because it can reveal the whole proxy chain to the client π and as a side effect, be extra careful in hosting environments: don't allow the TRACE request where the Forwarded field is used, because it would show up in the body of the response message π¨
8.3. Privacy Considerations
privacy has been a huge conversation lately π«Ά there's a trade-off between keeping users private and disclosing info that's useful for things like debugging, statistics, and making location-dependent content π the Forwarded header field, by design, exposes info that some users consider privacy sensitive, because that's how those useful things happen β¨
for any proxy: if the HTTP request contains header fields that specifically ask for privacy semantics, the proxy SHOULD NOT use the Forwarded header field, and it shouldn't pass private info like IP addresses on to the next hop either π that's the vibe we want π
the client's IP address, which may be forwarded in the for parameter, is considered privacy sensitive by many people π it can uniquely identify a client, reveal what operator they're using, and roughly estimate where they are geographically π
proxies using this extension will preserve the info of a direct connection π this has an end-user privacy impact whether or not the end-user or the deployer knows or expects it π so implementers and deployers of these proxies need to think hard about whether, and how, this extension affects user privacy π§
the default configuration for both the by and for parameters SHOULD contain obfuscated identifiers β¨ these identifiers SHOULD be randomly generated per request π² if identifiers that persist across requests are required, their lifetimes SHOULD be limited, and they SHOULD NOT persist longer than client IP addresses π«Ά when generating obfuscated identifiers, care must be taken not to include potentially sensitive info in them π
note that users' IP addresses may already be forwarded by proxies using the X-Forwarded-For header field, which is super widely used πΈ it's also worth knowing that if the user connected directly without passing through a proxy, their IP address would be sent to the web server anyway π¬ so users who don't actively choose an anonymizing proxy can't count on having their IP shielded π
users who want to minimize the risk of being tracked should also know there are other ways info can leak, like browser header field fingerprinting π΅οΈ and even without a uniquely identifying client identifier, the Forwarded header field may make fingerprinting easier, because it reveals the chain of proxies the client's request went through π¦
9. IANA Considerations π
this document specifies the HTTP header field listed below, which has been added to the "Permanent Message Header Field Names" registry defined in [RFC3864] π
Header field: Forwarded
Applicable protocol: http
Status: standard
Author/Change controller:
IETF (iesg@ietf.org)
Internet Engineering Task Force
Specification document(s): this specification (Section 4)
Related information: None
the Forwarded header field has parameters, and IANA created (and still maintains!) a brand new registry for them called "HTTP Forwarded Parameters" ποΈβ¨ the initial registrations are below. for future assignments, the registration procedure is IETF Review [RFC5226] π the security and privacy implications of every new parameter should be thoroughly documented π and new parameters and their values MUST conform with the forwarded-pair as defined in ABNF in Section 4 π also, the registration should include a short description π·
+-------------+---------------------------------------+-------------+
| Parameter | Description | Reference |
| name | | |
+-------------+---------------------------------------+-------------+
| by | IP address of incoming interface of a | Section 5.1 |
| | proxy | |
| for | IP address of client making a request | Section 5.2 |
| | through a proxy | |
| host | Host header field of the incoming | Section 5.3 |
| | request | |
| proto | Application protocol used for | Section 5.4 |
| | incoming request | |
+-------------+---------------------------------------+-------------+
Table 1: Initial Assignments
10. References π
10.1. Normative References
[RFC1918] Rekhter, Y., Moskowitz, R., Karrenberg, D., Groot, G., and
E. Lear, "Address Allocation for Private Internets",
BCP 5, RFC 1918, February 1996.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC3864] Klyne, G., Nottingham, M., and J. Mogul, "Registration
Procedures for Message Header Fields", BCP 90, RFC 3864,
September 2004.
[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
Resource Identifier (URI): Generic Syntax", STD 66,
RFC 3986, January 2005.
[RFC4193] Hinden, R. and B. Haberman, "Unique Local IPv6 Unicast
Addresses", RFC 4193, October 2005.
[RFC4395] Hansen, T., Hardie, T., and L. Masinter, "Guidelines and
Registration Procedures for New URI Schemes", BCP 35,
RFC 4395, February 2006.
[RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
IANA Considerations Section in RFCs", BCP 26, RFC 5226,
May 2008.
[RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
Specifications: ABNF", STD 68, RFC 5234, January 2008.
[RFC5952] Kawamura, S. and M. Kawashima, "A Recommendation for IPv6
Address Text Representation", RFC 5952, August 2010.
[RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
Protocol (HTTP/1.1): Message Syntax and Routing",
RFC 7230, June 2014.
[RFC7232] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
Protocol (HTTP/1.1): Conditional Requests", RFC 7232,
June 2014.
10.2. Informative References
[RFC6269] Ford, M., Boucadair, M., Durand, A., Levis, P., and P.
Roberts, "Issues with IP Address Sharing", RFC 6269,
June 2011.
Appendix A. Acknowledgments π«Ά
thank you thank you π to everyone who gave feedback: Per Cederqvist, Alissa Cooper, Adrian Farrel, Stephen Farrell, Ned Freed, Per Hedbor, Amos Jeffries, Poul-Henning Kamp, Murray S. Kucherawy, Barry Leiba, Salvatore Loreto, Alexey Melnikov, S. Moonesamy, Susan Nichols, Mark Nottingham, Julian Reschke, John Sullivan, Willy Tarreau, and Dan Wing πβ¨ you're all iconic π
Authors' Addresses π¦
Andreas Petersson
Opera Software
EMail: andreas@sbin.se
Martin Nilsson
Opera Software
S:t Larsgatan 12
Linkoping SE-582 24
EMail: nilsson@opera.com
π¦ where this page comes from
- ietf: rfc7239 2014-06
- π girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms