Skip to content

Commit 8c206bf

Browse files
committed
Improve docs on forwarded headers
Issue: SPR-15612
1 parent 6aeb8ef commit 8c206bf

File tree

4 files changed

+126
-11
lines changed

4 files changed

+126
-11
lines changed

spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ public static UriComponentsBuilder fromHttpUrl(String httpUrl) {
274274
/**
275275
* Create a new {@code UriComponents} object from the URI associated with
276276
* the given HttpRequest while also overlaying with values from the headers
277-
* "Forwarded" (<a href="http://tools.ietf.org/html/rfc7239">RFC 7239</a>,
277+
* "Forwarded" (<a href="http://tools.ietf.org/html/rfc7239">RFC 7239</a>),
278278
* or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if
279279
* "Forwarded" is not found.
280280
* @param request the source request

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java

+58-9
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,28 @@
6868

6969
/**
7070
* Creates instances of {@link org.springframework.web.util.UriComponentsBuilder}
71-
* by pointing to Spring MVC controllers and {@code @RequestMapping} methods.
71+
* by pointing to {@code @RequestMapping} methods on Spring MVC controllers.
7272
*
73-
* <p>The static {@code fromXxx(...)} methods prepare links relative to the
74-
* current request as determined by a call to
73+
* <p>There are several groups of methods:
74+
* <ul>
75+
* <li>Static {@code fromXxx(...)} methods to prepare links using information
76+
* from the current request as determined by a call to
7577
* {@link org.springframework.web.servlet.support.ServletUriComponentsBuilder#fromCurrentServletMapping()}.
78+
* <li>Static {@code fromXxx(UriComponentsBuilder,...)} methods can be given
79+
* a baseUrl when operating outside the context of a request.
80+
* <li>Instance-based {@code withXxx(...)} methods where an instance of
81+
* MvcUriComponentsBuilder is created with a baseUrl via
82+
* {@link #relativeTo(org.springframework.web.util.UriComponentsBuilder)}.
83+
* </ul>
7684
*
77-
* <p>The static {@code fromXxx(UriComponentsBuilder,...)} methods can be given
78-
* the baseUrl when operating outside the context of a request.
79-
*
80-
* <p>You can also create an MvcUriComponentsBuilder instance with a baseUrl
81-
* via {@link #relativeTo(org.springframework.web.util.UriComponentsBuilder)}
82-
* and then use the non-static {@code withXxx(...)} method variants.
85+
* <p><strong>Note:</strong> This class extracts and uses values from the headers
86+
* "Forwarded" (<a href="http://tools.ietf.org/html/rfc7239">RFC 7239</a>),
87+
* or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if
88+
* "Forwarded" is not found, in order to reflect the client-originated protocol
89+
* and address. As an alternative consider using the
90+
* {@link org.springframework.web.filter.ForwardedHeaderFilter} to have such
91+
* headers extracted once and removed, or removed only (without being used).
92+
* See the reference for further information including security considerations.
8393
*
8494
* @author Oliver Gierke
8595
* @author Rossen Stoyanchev
@@ -140,6 +150,8 @@ public static MvcUriComponentsBuilder relativeTo(UriComponentsBuilder baseUrl) {
140150
* Create a {@link UriComponentsBuilder} from the mapping of a controller class
141151
* and current request information including Servlet mapping. If the controller
142152
* contains multiple mappings, only the first one is used.
153+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
154+
* and "X-Forwarded-*" headers if found. See class-level docs.
143155
* @param controllerType the controller to build a URI for
144156
* @return a UriComponentsBuilder instance (never {@code null})
145157
*/
@@ -152,6 +164,8 @@ public static UriComponentsBuilder fromController(Class<?> controllerType) {
152164
* {@code UriComponentsBuilder} representing the base URL. This is useful
153165
* when using MvcUriComponentsBuilder outside the context of processing a
154166
* request or to apply a custom baseUrl not matching the current request.
167+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
168+
* and "X-Forwarded-*" headers if found. See class-level docs.
155169
* @param builder the builder for the base URL; the builder will be cloned
156170
* and therefore not modified and may be re-used for further calls.
157171
* @param controllerType the controller to build a URI for
@@ -169,6 +183,8 @@ public static UriComponentsBuilder fromController(UriComponentsBuilder builder,
169183
* Create a {@link UriComponentsBuilder} from the mapping of a controller
170184
* method and an array of method argument values. This method delegates
171185
* to {@link #fromMethod(Class, Method, Object...)}.
186+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
187+
* and "X-Forwarded-*" headers if found. See class-level docs.
172188
* @param controllerType the controller
173189
* @param methodName the method name
174190
* @param args the argument values
@@ -188,6 +204,8 @@ public static UriComponentsBuilder fromMethodName(Class<?> controllerType,
188204
* accepts a {@code UriComponentsBuilder} representing the base URL. This is
189205
* useful when using MvcUriComponentsBuilder outside the context of processing
190206
* a request or to apply a custom baseUrl not matching the current request.
207+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
208+
* and "X-Forwarded-*" headers if found. See class-level docs.
191209
* @param builder the builder for the base URL; the builder will be cloned
192210
* and therefore not modified and may be re-used for further calls.
193211
* @param controllerType the controller
@@ -235,6 +253,10 @@ public static UriComponentsBuilder fromMethodName(UriComponentsBuilder builder,
235253
* controller.getAddressesForCountry("US")
236254
* builder = MvcUriComponentsBuilder.fromMethodCall(controller);
237255
* </pre>
256+
*
257+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
258+
* and "X-Forwarded-*" headers if found. See class-level docs.
259+
*
238260
* @param info either the value returned from a "mock" controller
239261
* invocation or the "mock" controller itself after an invocation
240262
* @return a UriComponents instance
@@ -253,6 +275,8 @@ public static UriComponentsBuilder fromMethodCall(Object info) {
253275
* {@code UriComponentsBuilder} representing the base URL. This is useful
254276
* when using MvcUriComponentsBuilder outside the context of processing a
255277
* request or to apply a custom baseUrl not matching the current request.
278+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
279+
* and "X-Forwarded-*" headers if found. See class-level docs.
256280
* @param builder the builder for the base URL; the builder will be cloned
257281
* and therefore not modified and may be re-used for further calls.
258282
* @param info either the value returned from a "mock" controller
@@ -303,6 +327,10 @@ public static UriComponentsBuilder fromMethodCall(UriComponentsBuilder builder,
303327
* </pre>
304328
* <p>Note that it's not necessary to specify all arguments. Only the ones
305329
* required to prepare the URL, mainly {@code @RequestParam} and {@code @PathVariable}).
330+
*
331+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
332+
* and "X-Forwarded-*" headers if found. See class-level docs.
333+
*
306334
* @param mappingName the mapping name
307335
* @return a builder to prepare the URI String
308336
* @throws IllegalArgumentException if the mapping name is not found or
@@ -318,6 +346,8 @@ public static MethodArgumentBuilder fromMappingName(String mappingName) {
318346
* {@code UriComponentsBuilder} representing the base URL. This is useful
319347
* when using MvcUriComponentsBuilder outside the context of processing a
320348
* request or to apply a custom baseUrl not matching the current request.
349+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
350+
* and "X-Forwarded-*" headers if found. See class-level docs.
321351
* @param builder the builder for the base URL; the builder will be cloned
322352
* and therefore not modified and may be re-used for further calls.
323353
* @param name the mapping name
@@ -350,6 +380,8 @@ public static MethodArgumentBuilder fromMappingName(UriComponentsBuilder builder
350380
* {@link org.springframework.web.method.support.UriComponentsContributor
351381
* UriComponentsContributor}) while remaining argument values are ignored and
352382
* can be {@code null}.
383+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
384+
* and "X-Forwarded-*" headers if found. See class-level docs.
353385
* @param controllerType the controller type
354386
* @param method the controller method
355387
* @param args argument values for the controller method
@@ -366,6 +398,8 @@ public static UriComponentsBuilder fromMethod(Class<?> controllerType, Method me
366398
* This is useful when using MvcUriComponentsBuilder outside the context of
367399
* processing a request or to apply a custom baseUrl not matching the
368400
* current request.
401+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
402+
* and "X-Forwarded-*" headers if found. See class-level docs.
369403
* @param baseUrl the builder for the base URL; the builder will be cloned
370404
* and therefore not modified and may be re-used for further calls.
371405
* @param controllerType the controller type
@@ -557,6 +591,9 @@ private static WebApplicationContext getWebApplicationContext() {
557591
* <pre class="code">
558592
* MvcUriComponentsBuilder.fromMethodCall(on(FooController.class).getFoo(1)).build();
559593
* </pre>
594+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
595+
* and "X-Forwarded-*" headers if found. See class-level docs.
596+
*
560597
* @param controllerType the target controller
561598
*/
562599
public static <T> T on(Class<T> controllerType) {
@@ -579,6 +616,8 @@ public static <T> T on(Class<T> controllerType) {
579616
* fooController.saveFoo(2, null);
580617
* builder = MvcUriComponentsBuilder.fromMethodCall(fooController);
581618
* </pre>
619+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
620+
* and "X-Forwarded-*" headers if found. See class-level docs.
582621
* @param controllerType the target controller
583622
*/
584623
public static <T> T controller(Class<T> controllerType) {
@@ -634,6 +673,8 @@ private static <T> T initProxy(Class<?> type, ControllerMethodInvocationIntercep
634673
/**
635674
* An alternative to {@link #fromController(Class)} for use with an instance
636675
* of this class created via a call to {@link #relativeTo}.
676+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
677+
* and "X-Forwarded-*" headers if found. See class-level docs.
637678
* @since 4.2
638679
*/
639680
public UriComponentsBuilder withController(Class<?> controllerType) {
@@ -643,6 +684,8 @@ public UriComponentsBuilder withController(Class<?> controllerType) {
643684
/**
644685
* An alternative to {@link #fromMethodName(Class, String, Object...)}} for
645686
* use with an instance of this class created via {@link #relativeTo}.
687+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
688+
* and "X-Forwarded-*" headers if found. See class-level docs.
646689
* @since 4.2
647690
*/
648691
public UriComponentsBuilder withMethodName(Class<?> controllerType, String methodName, Object... args) {
@@ -652,6 +695,8 @@ public UriComponentsBuilder withMethodName(Class<?> controllerType, String metho
652695
/**
653696
* An alternative to {@link #fromMethodCall(Object)} for use with an instance
654697
* of this class created via {@link #relativeTo}.
698+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
699+
* and "X-Forwarded-*" headers if found. See class-level docs.
655700
* @since 4.2
656701
*/
657702
public UriComponentsBuilder withMethodCall(Object invocationInfo) {
@@ -661,6 +706,8 @@ public UriComponentsBuilder withMethodCall(Object invocationInfo) {
661706
/**
662707
* An alternative to {@link #fromMappingName(String)} for use with an instance
663708
* of this class created via {@link #relativeTo}.
709+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
710+
* and "X-Forwarded-*" headers if found. See class-level docs.
664711
* @since 4.2
665712
*/
666713
public MethodArgumentBuilder withMappingName(String mappingName) {
@@ -670,6 +717,8 @@ public MethodArgumentBuilder withMappingName(String mappingName) {
670717
/**
671718
* An alternative to {@link #fromMethod(Class, Method, Object...)}
672719
* for use with an instance of this class created via {@link #relativeTo}.
720+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
721+
* and "X-Forwarded-*" headers if found. See class-level docs.
673722
* @since 4.2
674723
*/
675724
public UriComponentsBuilder withMethod(Class<?> controllerType, Method method, Object... args) {

spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java

+35-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,17 @@
3232
import org.springframework.web.util.UrlPathHelper;
3333

3434
/**
35-
* A UriComponentsBuilder that extracts information from the HttpServletRequest.
35+
* UriComponentsBuilder with additional static factory methods to create links
36+
* based on the current HttpServletRequest.
37+
*
38+
* <p><strong>Note:</strong> This class extracts and uses values from the headers
39+
* "Forwarded" (<a href="http://tools.ietf.org/html/rfc7239">RFC 7239</a>),
40+
* or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if
41+
* "Forwarded" is not found, in order to reflect the client-originated protocol
42+
* and address. As an alternative consider using the
43+
* {@link org.springframework.web.filter.ForwardedHeaderFilter} to have such
44+
* headers extracted once and removed, or removed only (without being used).
45+
* See the reference for further information including security considerations.
3646
*
3747
* @author Rossen Stoyanchev
3848
* @since 3.1
@@ -69,6 +79,9 @@ protected ServletUriComponentsBuilder(ServletUriComponentsBuilder other) {
6979
/**
7080
* Prepare a builder from the host, port, scheme, and context path of the
7181
* given HttpServletRequest.
82+
*
83+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
84+
* and "X-Forwarded-*" headers if found. See class-level docs.
7285
*/
7386
public static ServletUriComponentsBuilder fromContextPath(HttpServletRequest request) {
7487
ServletUriComponentsBuilder builder = initFromRequest(request);
@@ -83,6 +96,9 @@ public static ServletUriComponentsBuilder fromContextPath(HttpServletRequest req
8396
* will end with "/main". If the servlet is mapped otherwise, e.g.
8497
* {@code "/"} or {@code "*.do"}, the result will be the same as
8598
* if calling {@link #fromContextPath(HttpServletRequest)}.
99+
*
100+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
101+
* and "X-Forwarded-*" headers if found. See class-level docs.
86102
*/
87103
public static ServletUriComponentsBuilder fromServletMapping(HttpServletRequest request) {
88104
ServletUriComponentsBuilder builder = fromContextPath(request);
@@ -95,6 +111,9 @@ public static ServletUriComponentsBuilder fromServletMapping(HttpServletRequest
95111
/**
96112
* Prepare a builder from the host, port, scheme, and path (but not the query)
97113
* of the HttpServletRequest.
114+
*
115+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
116+
* and "X-Forwarded-*" headers if found. See class-level docs.
98117
*/
99118
public static ServletUriComponentsBuilder fromRequestUri(HttpServletRequest request) {
100119
ServletUriComponentsBuilder builder = initFromRequest(request);
@@ -105,6 +124,9 @@ public static ServletUriComponentsBuilder fromRequestUri(HttpServletRequest requ
105124
/**
106125
* Prepare a builder by copying the scheme, host, port, path, and
107126
* query string of an HttpServletRequest.
127+
*
128+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
129+
* and "X-Forwarded-*" headers if found. See class-level docs.
108130
*/
109131
public static ServletUriComponentsBuilder fromRequest(HttpServletRequest request) {
110132
ServletUriComponentsBuilder builder = initFromRequest(request);
@@ -153,6 +175,9 @@ private static String prependForwardedPrefix(HttpServletRequest request, String
153175
/**
154176
* Same as {@link #fromContextPath(HttpServletRequest)} except the
155177
* request is obtained through {@link RequestContextHolder}.
178+
*
179+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
180+
* and "X-Forwarded-*" headers if found. See class-level docs.
156181
*/
157182
public static ServletUriComponentsBuilder fromCurrentContextPath() {
158183
return fromContextPath(getCurrentRequest());
@@ -161,6 +186,9 @@ public static ServletUriComponentsBuilder fromCurrentContextPath() {
161186
/**
162187
* Same as {@link #fromServletMapping(HttpServletRequest)} except the
163188
* request is obtained through {@link RequestContextHolder}.
189+
*
190+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
191+
* and "X-Forwarded-*" headers if found. See class-level docs.
164192
*/
165193
public static ServletUriComponentsBuilder fromCurrentServletMapping() {
166194
return fromServletMapping(getCurrentRequest());
@@ -169,6 +197,9 @@ public static ServletUriComponentsBuilder fromCurrentServletMapping() {
169197
/**
170198
* Same as {@link #fromRequestUri(HttpServletRequest)} except the
171199
* request is obtained through {@link RequestContextHolder}.
200+
*
201+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
202+
* and "X-Forwarded-*" headers if found. See class-level docs.
172203
*/
173204
public static ServletUriComponentsBuilder fromCurrentRequestUri() {
174205
return fromRequestUri(getCurrentRequest());
@@ -177,6 +208,9 @@ public static ServletUriComponentsBuilder fromCurrentRequestUri() {
177208
/**
178209
* Same as {@link #fromRequest(HttpServletRequest)} except the
179210
* request is obtained through {@link RequestContextHolder}.
211+
*
212+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
213+
* and "X-Forwarded-*" headers if found. See class-level docs.
180214
*/
181215
public static ServletUriComponentsBuilder fromCurrentRequest() {
182216
return fromRequest(getCurrentRequest());

src/asciidoc/web-mvc.adoc

+32
Original file line numberDiff line numberDiff line change
@@ -3389,6 +3389,38 @@ with a base URL and then use the instance-based "withXxx" methods. For example:
33893389
----
33903390

33913391

3392+
[[mvc-links-to-controllers-forwarded-headers]]
3393+
=== Working with "Forwarded" and "X-Forwarded-*" Headers
3394+
3395+
As a request goes through proxies such as load balancers the host, port, and
3396+
scheme may change presenting a challenge for applications that need to create links
3397+
to resources since the links should reflect the host, port, and scheme of the
3398+
original request as seen from a client perspective.
3399+
3400+
https://tools.ietf.org/html/rfc7239[RFC 7239] defines the "Forwarded" HTTP header
3401+
for proxies to use to provide information about the original request. There are also
3402+
other non-standard headers in use such as "X-Forwarded-Host", "X-Forwarded-Port",
3403+
and "X-Forwarded-Proto".
3404+
3405+
Both `ServletUriComponentsBuilder` and `MvcUriComponentsBuilder` detect, extract, and use
3406+
information from the "Forwarded" header, or from "X-Forwarded-Host", "X-Forwarded-Port",
3407+
and "X-Forwarded-Proto" if "Forwarded" is not present, so that the resulting links reflect
3408+
the original request.
3409+
3410+
The `ForwardedHeaderFilter` provides an alternative to do the same once and globally for
3411+
the entire application. The filter wraps the request in order to overlay host, port, and
3412+
scheme information and also "hides" any forwarded headers for subsequent processing.
3413+
3414+
Note that there are security considerations when using forwarded headers as explained
3415+
in Section 8 of RFC 7239. At the application level it is difficult to determine whether
3416+
forwarded headers can be trusted or not. This is why the network upstream should be
3417+
configured correctly to filter out untrusted forwarded headers from the outside.
3418+
3419+
Applications that don't have a proxy and don't need to use forwarded headers can
3420+
configure the `ForwardedHeaderFilter` to remove and ignore such headers.
3421+
3422+
3423+
33923424
[[mvc-links-to-controllers-from-views]]
33933425
=== Building URIs to Controllers and methods from views
33943426

0 commit comments

Comments
 (0)