Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document X-Forwarded-* Headers #31491

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions framework-docs/modules/ROOT/pages/web/webflux/reactive-spring.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -368,31 +368,34 @@ collecting to a `MultiValueMap`.
=== Forwarded Headers
[.small]#xref:web/webmvc/filters.adoc#filters-forwarded-headers[See equivalent in the Servlet stack]#

As a request goes through proxies (such as load balancers), the host, port, and
scheme may change. That makes it a challenge, from a client perspective, to create links that point to the correct
host, port, and scheme.
include::partial$web/forwarded-headers.adoc[]

https://tools.ietf.org/html/rfc7239[RFC 7239] defines the `Forwarded` HTTP header
that proxies can use to provide information about the original request. There are other
non-standard headers, too, including `X-Forwarded-Host`, `X-Forwarded-Port`,
`X-Forwarded-Proto`, `X-Forwarded-Ssl`, and `X-Forwarded-Prefix`.


[[webflux-forwarded-headers-forwardedheadertransformer]]
=== ForwardedHeaderTransformer

`ForwardedHeaderTransformer` is a component that modifies the host, port, and scheme of
the request, based on forwarded headers, and then removes those headers. If you declare
it as a bean with the name `forwardedHeaderTransformer`, it will be
xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api-special-beans[detected] and used.

NOTE: In 5.1 `ForwardedHeaderFilter` was deprecated and superseded by
`ForwardedHeaderTransformer` so forwarded headers can be processed earlier, before the
exchange is created. If the filter is configured anyway, it is taken out of the list of
filters, and `ForwardedHeaderTransformer` is used instead.



[[webflux-forwarded-headers-security]]
=== Security Considerations

There are security considerations for forwarded headers, since an application cannot know
if the headers were added by a proxy, as intended, or by a malicious client. This is why
a proxy at the boundary of trust should be configured to remove untrusted forwarded traffic coming
from the outside. You can also configure the `ForwardedHeaderTransformer` with
`removeOnly=true`, in which case it removes but does not use the headers.

NOTE: In 5.1 `ForwardedHeaderFilter` was deprecated and superseded by
`ForwardedHeaderTransformer` so forwarded headers can be processed earlier, before the
exchange is created. If the filter is configured anyway, it is taken out of the list of
filters, and `ForwardedHeaderTransformer` is used instead.



[[webflux-filters]]
Expand Down
24 changes: 16 additions & 8 deletions framework-docs/modules/ROOT/pages/web/webmvc/filters.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,39 @@ available through the `ServletRequest.getParameter{asterisk}()` family of method



[[filters-forwarded-headers]]
[[forwarded-headers]]
== Forwarded Headers
[.small]#xref:web/webflux/reactive-spring.adoc#webflux-forwarded-headers[See equivalent in the Reactive stack]#

As a request goes through proxies (such as load balancers) the host, port, and
scheme may change, and that makes it a challenge to create links that point to the correct
host, port, and scheme from a client perspective.
include::partial$web/forwarded-headers.adoc[]

https://tools.ietf.org/html/rfc7239[RFC 7239] defines the `Forwarded` HTTP header
that proxies can use to provide information about the original request. There are other
non-standard headers, too, including `X-Forwarded-Host`, `X-Forwarded-Port`,
`X-Forwarded-Proto`, `X-Forwarded-Ssl`, and `X-Forwarded-Prefix`.


[[filters-forwarded-headers-non-forwardedheaderfilter]]
=== ForwardedHeaderFilter

`ForwardedHeaderFilter` is a Servlet filter that modifies the request in order to
a) change the host, port, and scheme based on `Forwarded` headers, and b) to remove those
headers to eliminate further impact. The filter relies on wrapping the request, and
therefore it must be ordered ahead of other filters, such as `RequestContextFilter`, that
should work with the modified and not the original request.



[[filters-forwarded-headers-security]]
=== Security Considerations

There are security considerations for forwarded headers since an application cannot know
if the headers were added by a proxy, as intended, or by a malicious client. This is why
a proxy at the boundary of trust should be configured to remove untrusted `Forwarded`
headers that come from the outside. You can also configure the `ForwardedHeaderFilter`
with `removeOnly=true`, in which case it removes but does not use the headers.



[[filters-forwarded-headers-dispatcher]]
=== About Dispatcher Types

In order to support xref:web/webmvc/mvc-ann-async.adoc[asynchronous requests] and error dispatches this
filter should be mapped with `DispatcherType.ASYNC` and also `DispatcherType.ERROR`.
If using Spring Framework's `AbstractAnnotationConfigDispatcherServletInitializer`
Expand Down
135 changes: 135 additions & 0 deletions framework-docs/modules/ROOT/partials/web/forwarded-headers.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
As a request goes through proxies (such as load balancers) the host, port, and
scheme may change, and that makes it a challenge to create links that point to the correct
host, port, and scheme from a client perspective.

https://tools.ietf.org/html/rfc7239[RFC 7239] defines the `Forwarded` HTTP header
that proxies can use to provide information about the original request.



[[forwarded-headers-non-standard]]
=== Non-standard Headers

There are other non-standard headers, too, including `X-Forwarded-Host`, `X-Forwarded-Port`,
`X-Forwarded-Proto`, `X-Forwarded-Ssl`, and `X-Forwarded-Prefix`.



[[x-forwarded-host]]
==== X-Forwarded-Host

While not standard, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host[`X-Forwarded-Host: <host>`]
is a de-facto standard header that is used to communicate the original host to a
downstream server. For example, if a request of `https://example.com/resource` is sent to
a proxy which forwards the request to `http://localhost:8080/resource`, then a header of
`X-Forwarded-Host: example.com` can be sent to inform the server that the original host was `example.com`.



[[x-forwarded-port]]
==== X-Forwarded-Port

While not standard, `X-Forwarded-Port: <port>` is a de-facto standard header that is used to
communicate the original port to a downstream server. For example, if a request of
`https://example.com/resource` is sent to a proxy which forwards the request to
`http://localhost:8080/resource`, then a header of `X-Forwarded-Port: 443` can be sent
to inform the server that the original port was `443`.



[[x-forwarded-proto]]
==== X-Forwarded-Proto

While not standard, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto[`X-Forwarded-Proto: (https|http)`]
is a de-facto standard header that is used to communicate the original protocol (e.g. https / https)
to a downstream server. For example, if a request of `https://example.com/resource` is sent to
a proxy which forwards the request to `http://localhost:8080/resource`, then a header of
`X-Forwarded-Proto: https` can be sent to inform the server that the original protocol was `https`.



[[x-forwarded-ssl]]
==== X-Forwarded-Ssl

While not standard, `X-Forwarded-Ssl: (on|off)` is a de-facto standard header that is used to communicate the
original protocol (e.g. https / https) to a downstream server. For example, if a request of
`https://example.com/resource` is sent to a proxy which forwards the request to
`http://localhost:8080/resource`, then a header of `X-Forwarded-Ssl: on` to inform the server that the
original protocol was `https`.



[[x-forwarded-prefix]]
==== X-Forwarded-Prefix

While not standard, https://microsoft.github.io/reverse-proxy/articles/transforms.html#defaults[`X-Forwarded-Prefix: <prefix>`]
is a de-facto standard header that is used to communicate the original URL path prefix to a
downstream server.

The definition of the path prefix is most easily defined by an example. For example, consider
the following proxy to server mapping of:

[subs="-attributes"]
----
https://example.com/api/{path} -> http://localhost:8080/app1/{path}
----

The prefix is defined as the porition of the URL path before the capture group of `+{path}+`.
For the proxy, the prefix is `/api` and for the server the prefix is `/app1`. In this case,
the header of `X-Forwarded-Prefix: /api` can be sent to indicate the original prefix of `/api`
which overrides the server's prefix of `/app1`.

The `X-Forwarded-Prefix` is flexible because it overrides the existing prefix. This means that
the server prefix can be replaced (as demonstrated above), removed, or modified.

The previous example demonstrated how to replace the prefix, but at times users may want to
instruct the server to remove the prefix. For example, consider the proxy to server
mapping of:

[subs="-attributes"]
----
https://app1.example.com/{path} -> http://localhost:8080/app1/{path}
https://app2.example.com/{path} -> http://localhost:8080/app2/{path}
----

In the `app1` example above, the proxy has an empty prefix and the server has a prefix of
`/app1`. The header of ``X-Forwarded-Prefix: `` can be sent to indicate the original empty
prefix which overrides the server's prefix of `/app1`. In the `app2` example above, the proxy
has an empty prefix and the server has a prefix of `/app2`. The header of ``X-Forwarded-Prefix: ``
can be sent to indicate the original empty prefix which overrides the server's prefix of `/app2`.

[NOTE]
====
A common usecase is that an organization pays licenses per production application server.
This means that they prefer to deploy multiple applications to each application server to
avoid paying the licensing fees.

Another common usecase is that organizations may be using more resource intensive
application servers. This means that they prefer to deploy multiple applications to each
application server to avoid consuming additional resources.

In both of these usecases, applications must define a non-empty context root because there is
more than one application associated to the same application server.

While their application is deployed with a non-empty context root, they do not want this
expressed in the path of their URLs because they use a different subdomain for each application.
Using different subdomains for each application provides benefits such as:

* Added security (e.g. same origin policy)
* Allows for scaling the applications differently (a different domain can point to different
IP addresses)

The example above illustrates how to implement such a scenario.
====

In some cases, a proxy may want to insert a prefix in front of the existing prefix. For
example, consider the proxy to server mapping of:

[subs="-attributes"]
----
https://example.com/api/app1/{path} -> http://localhost:8080/app1/{path}
----

In the example above, the proxy has a prefix of `/api/app1` and the server has a prefix of
`/app1`. The header of `X-Forwarded-Prefix: /api/app1` can be sent to indicate the original
prefix of `/api/app1` which overrides the server's prefix of `/app1`.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
* @author Rob Winch
* @since 4.3
* @see <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a>
* @see <a href="https://docs.spring.io/spring-framework/reference/web/webmvc/filters.html#filters-forwarded-headers">Forwarded Headers</a>
*/
public class ForwardedHeaderFilter extends OncePerRequestFilter {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
* @author Rossen Stoyanchev
* @since 5.1
* @see <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a>
* @see <a href="https://docs.spring.io/spring-framework/reference/web/webflux/reactive-spring.html#webflux-forwarded-headers">Forwarded Headers</a>
*/
public class ForwardedHeaderTransformer implements Function<ServerHttpRequest, ServerHttpRequest> {

Expand Down