Skip to content

Commit 4d52590

Browse files
committed
Improve docs on forwarded headers
Issue: SPR-15612
1 parent 895fa2e commit 4d52590

File tree

4 files changed

+126
-12
lines changed

4 files changed

+126
-12
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ public static UriComponentsBuilder fromHttpUrl(String httpUrl) {
275275
/**
276276
* Create a new {@code UriComponents} object from the URI associated with
277277
* the given HttpRequest while also overlaying with values from the headers
278-
* "Forwarded" (<a href="http://tools.ietf.org/html/rfc7239">RFC 7239</a>,
278+
* "Forwarded" (<a href="http://tools.ietf.org/html/rfc7239">RFC 7239</a>),
279279
* or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if
280280
* "Forwarded" is not found.
281281
* @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
@@ -70,18 +70,28 @@
7070

7171
/**
7272
* Creates instances of {@link org.springframework.web.util.UriComponentsBuilder}
73-
* by pointing to Spring MVC controllers and {@code @RequestMapping} methods.
73+
* by pointing to {@code @RequestMapping} methods on Spring MVC controllers.
7474
*
75-
* <p>The static {@code fromXxx(...)} methods prepare links relative to the
76-
* current request as determined by a call to
75+
* <p>There are several groups of methods:
76+
* <ul>
77+
* <li>Static {@code fromXxx(...)} methods to prepare links using information
78+
* from the current request as determined by a call to
7779
* {@link org.springframework.web.servlet.support.ServletUriComponentsBuilder#fromCurrentServletMapping()}.
80+
* <li>Static {@code fromXxx(UriComponentsBuilder,...)} methods can be given
81+
* a baseUrl when operating outside the context of a request.
82+
* <li>Instance-based {@code withXxx(...)} methods where an instance of
83+
* MvcUriComponentsBuilder is created with a baseUrl via
84+
* {@link #relativeTo(org.springframework.web.util.UriComponentsBuilder)}.
85+
* </ul>
7886
*
79-
* <p>The static {@code fromXxx(UriComponentsBuilder,...)} methods can be given
80-
* the baseUrl when operating outside the context of a request.
81-
*
82-
* <p>You can also create an MvcUriComponentsBuilder instance with a baseUrl
83-
* via {@link #relativeTo(org.springframework.web.util.UriComponentsBuilder)}
84-
* and then use the non-static {@code withXxx(...)} method variants.
87+
* <p><strong>Note:</strong> This class extracts and uses values from the headers
88+
* "Forwarded" (<a href="http://tools.ietf.org/html/rfc7239">RFC 7239</a>),
89+
* or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if
90+
* "Forwarded" is not found, in order to reflect the client-originated protocol
91+
* and address. As an alternative consider using the
92+
* {@link org.springframework.web.filter.ForwardedHeaderFilter} to have such
93+
* headers extracted once and removed, or removed only (without being used).
94+
* See the reference for further information including security considerations.
8595
*
8696
* @author Oliver Gierke
8797
* @author Rossen Stoyanchev
@@ -142,6 +152,8 @@ public static MvcUriComponentsBuilder relativeTo(UriComponentsBuilder baseUrl) {
142152
* Create a {@link UriComponentsBuilder} from the mapping of a controller class
143153
* and current request information including Servlet mapping. If the controller
144154
* contains multiple mappings, only the first one is used.
155+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
156+
* and "X-Forwarded-*" headers if found. See class-level docs.
145157
* @param controllerType the controller to build a URI for
146158
* @return a UriComponentsBuilder instance (never {@code null})
147159
*/
@@ -154,6 +166,8 @@ public static UriComponentsBuilder fromController(Class<?> controllerType) {
154166
* {@code UriComponentsBuilder} representing the base URL. This is useful
155167
* when using MvcUriComponentsBuilder outside the context of processing a
156168
* request or to apply a custom baseUrl not matching the current request.
169+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
170+
* and "X-Forwarded-*" headers if found. See class-level docs.
157171
* @param builder the builder for the base URL; the builder will be cloned
158172
* and therefore not modified and may be re-used for further calls.
159173
* @param controllerType the controller to build a URI for
@@ -171,6 +185,8 @@ public static UriComponentsBuilder fromController(@Nullable UriComponentsBuilder
171185
* Create a {@link UriComponentsBuilder} from the mapping of a controller
172186
* method and an array of method argument values. This method delegates
173187
* to {@link #fromMethod(Class, Method, Object...)}.
188+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
189+
* and "X-Forwarded-*" headers if found. See class-level docs.
174190
* @param controllerType the controller
175191
* @param methodName the method name
176192
* @param args the argument values
@@ -190,6 +206,8 @@ public static UriComponentsBuilder fromMethodName(Class<?> controllerType,
190206
* accepts a {@code UriComponentsBuilder} representing the base URL. This is
191207
* useful when using MvcUriComponentsBuilder outside the context of processing
192208
* a request or to apply a custom baseUrl not matching the current request.
209+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
210+
* and "X-Forwarded-*" headers if found. See class-level docs.
193211
* @param builder the builder for the base URL; the builder will be cloned
194212
* and therefore not modified and may be re-used for further calls.
195213
* @param controllerType the controller
@@ -237,6 +255,10 @@ public static UriComponentsBuilder fromMethodName(UriComponentsBuilder builder,
237255
* controller.getAddressesForCountry("US")
238256
* builder = MvcUriComponentsBuilder.fromMethodCall(controller);
239257
* </pre>
258+
*
259+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
260+
* and "X-Forwarded-*" headers if found. See class-level docs.
261+
*
240262
* @param info either the value returned from a "mock" controller
241263
* invocation or the "mock" controller itself after an invocation
242264
* @return a UriComponents instance
@@ -255,6 +277,8 @@ public static UriComponentsBuilder fromMethodCall(Object info) {
255277
* {@code UriComponentsBuilder} representing the base URL. This is useful
256278
* when using MvcUriComponentsBuilder outside the context of processing a
257279
* request or to apply a custom baseUrl not matching the current request.
280+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
281+
* and "X-Forwarded-*" headers if found. See class-level docs.
258282
* @param builder the builder for the base URL; the builder will be cloned
259283
* and therefore not modified and may be re-used for further calls.
260284
* @param info either the value returned from a "mock" controller
@@ -305,6 +329,10 @@ public static UriComponentsBuilder fromMethodCall(UriComponentsBuilder builder,
305329
* </pre>
306330
* <p>Note that it's not necessary to specify all arguments. Only the ones
307331
* required to prepare the URL, mainly {@code @RequestParam} and {@code @PathVariable}).
332+
*
333+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
334+
* and "X-Forwarded-*" headers if found. See class-level docs.
335+
*
308336
* @param mappingName the mapping name
309337
* @return a builder to prepare the URI String
310338
* @throws IllegalArgumentException if the mapping name is not found or
@@ -320,6 +348,8 @@ public static MethodArgumentBuilder fromMappingName(String mappingName) {
320348
* {@code UriComponentsBuilder} representing the base URL. This is useful
321349
* when using MvcUriComponentsBuilder outside the context of processing a
322350
* request or to apply a custom baseUrl not matching the current request.
351+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
352+
* and "X-Forwarded-*" headers if found. See class-level docs.
323353
* @param builder the builder for the base URL; the builder will be cloned
324354
* and therefore not modified and may be re-used for further calls.
325355
* @param name the mapping name
@@ -352,6 +382,8 @@ public static MethodArgumentBuilder fromMappingName(@Nullable UriComponentsBuild
352382
* {@link org.springframework.web.method.support.UriComponentsContributor
353383
* UriComponentsContributor}) while remaining argument values are ignored and
354384
* can be {@code null}.
385+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
386+
* and "X-Forwarded-*" headers if found. See class-level docs.
355387
* @param controllerType the controller type
356388
* @param method the controller method
357389
* @param args argument values for the controller method
@@ -368,6 +400,8 @@ public static UriComponentsBuilder fromMethod(Class<?> controllerType, Method me
368400
* This is useful when using MvcUriComponentsBuilder outside the context of
369401
* processing a request or to apply a custom baseUrl not matching the
370402
* current request.
403+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
404+
* and "X-Forwarded-*" headers if found. See class-level docs.
371405
* @param baseUrl the builder for the base URL; the builder will be cloned
372406
* and therefore not modified and may be re-used for further calls.
373407
* @param controllerType the controller type
@@ -549,6 +583,9 @@ private static WebApplicationContext getWebApplicationContext() {
549583
* <pre class="code">
550584
* MvcUriComponentsBuilder.fromMethodCall(on(FooController.class).getFoo(1)).build();
551585
* </pre>
586+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
587+
* and "X-Forwarded-*" headers if found. See class-level docs.
588+
*
552589
* @param controllerType the target controller
553590
*/
554591
public static <T> T on(Class<T> controllerType) {
@@ -571,6 +608,8 @@ public static <T> T on(Class<T> controllerType) {
571608
* fooController.saveFoo(2, null);
572609
* builder = MvcUriComponentsBuilder.fromMethodCall(fooController);
573610
* </pre>
611+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
612+
* and "X-Forwarded-*" headers if found. See class-level docs.
574613
* @param controllerType the target controller
575614
*/
576615
public static <T> T controller(Class<T> controllerType) {
@@ -626,6 +665,8 @@ private static <T> T initProxy(Class<?> type, ControllerMethodInvocationIntercep
626665
/**
627666
* An alternative to {@link #fromController(Class)} for use with an instance
628667
* of this class created via a call to {@link #relativeTo}.
668+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
669+
* and "X-Forwarded-*" headers if found. See class-level docs.
629670
* @since 4.2
630671
*/
631672
public UriComponentsBuilder withController(Class<?> controllerType) {
@@ -635,6 +676,8 @@ public UriComponentsBuilder withController(Class<?> controllerType) {
635676
/**
636677
* An alternative to {@link #fromMethodName(Class, String, Object...)}} for
637678
* use with an instance of this class created via {@link #relativeTo}.
679+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
680+
* and "X-Forwarded-*" headers if found. See class-level docs.
638681
* @since 4.2
639682
*/
640683
public UriComponentsBuilder withMethodName(Class<?> controllerType, String methodName, Object... args) {
@@ -644,6 +687,8 @@ public UriComponentsBuilder withMethodName(Class<?> controllerType, String metho
644687
/**
645688
* An alternative to {@link #fromMethodCall(Object)} for use with an instance
646689
* of this class created via {@link #relativeTo}.
690+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
691+
* and "X-Forwarded-*" headers if found. See class-level docs.
647692
* @since 4.2
648693
*/
649694
public UriComponentsBuilder withMethodCall(Object invocationInfo) {
@@ -653,6 +698,8 @@ public UriComponentsBuilder withMethodCall(Object invocationInfo) {
653698
/**
654699
* An alternative to {@link #fromMappingName(String)} for use with an instance
655700
* of this class created via {@link #relativeTo}.
701+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
702+
* and "X-Forwarded-*" headers if found. See class-level docs.
656703
* @since 4.2
657704
*/
658705
public MethodArgumentBuilder withMappingName(String mappingName) {
@@ -662,6 +709,8 @@ public MethodArgumentBuilder withMappingName(String mappingName) {
662709
/**
663710
* An alternative to {@link #fromMethod(Class, Method, Object...)}
664711
* for use with an instance of this class created via {@link #relativeTo}.
712+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
713+
* and "X-Forwarded-*" headers if found. See class-level docs.
665714
* @since 4.2
666715
*/
667716
public UriComponentsBuilder withMethod(Class<?> controllerType, Method method, Object... args) {

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

+35-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package org.springframework.web.servlet.support;
1818

1919
import java.util.Enumeration;
20-
2120
import javax.servlet.http.HttpServletRequest;
2221

2322
import org.springframework.http.HttpRequest;
@@ -34,7 +33,17 @@
3433
import org.springframework.web.util.UrlPathHelper;
3534

3635
/**
37-
* A UriComponentsBuilder that extracts information from the HttpServletRequest.
36+
* UriComponentsBuilder with additional static factory methods to create links
37+
* based on the current HttpServletRequest.
38+
*
39+
* <p><strong>Note:</strong> This class extracts and uses values from the headers
40+
* "Forwarded" (<a href="http://tools.ietf.org/html/rfc7239">RFC 7239</a>),
41+
* or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if
42+
* "Forwarded" is not found, in order to reflect the client-originated protocol
43+
* and address. As an alternative consider using the
44+
* {@link org.springframework.web.filter.ForwardedHeaderFilter} to have such
45+
* headers extracted once and removed, or removed only (without being used).
46+
* See the reference for further information including security considerations.
3847
*
3948
* @author Rossen Stoyanchev
4049
* @since 3.1
@@ -71,6 +80,9 @@ protected ServletUriComponentsBuilder(ServletUriComponentsBuilder other) {
7180
/**
7281
* Prepare a builder from the host, port, scheme, and context path of the
7382
* given HttpServletRequest.
83+
*
84+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
85+
* and "X-Forwarded-*" headers if found. See class-level docs.
7486
*/
7587
public static ServletUriComponentsBuilder fromContextPath(HttpServletRequest request) {
7688
ServletUriComponentsBuilder builder = initFromRequest(request);
@@ -85,6 +97,9 @@ public static ServletUriComponentsBuilder fromContextPath(HttpServletRequest req
8597
* will end with "/main". If the servlet is mapped otherwise, e.g.
8698
* {@code "/"} or {@code "*.do"}, the result will be the same as
8799
* if calling {@link #fromContextPath(HttpServletRequest)}.
100+
*
101+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
102+
* and "X-Forwarded-*" headers if found. See class-level docs.
88103
*/
89104
public static ServletUriComponentsBuilder fromServletMapping(HttpServletRequest request) {
90105
ServletUriComponentsBuilder builder = fromContextPath(request);
@@ -97,6 +112,9 @@ public static ServletUriComponentsBuilder fromServletMapping(HttpServletRequest
97112
/**
98113
* Prepare a builder from the host, port, scheme, and path (but not the query)
99114
* of the HttpServletRequest.
115+
*
116+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
117+
* and "X-Forwarded-*" headers if found. See class-level docs.
100118
*/
101119
public static ServletUriComponentsBuilder fromRequestUri(HttpServletRequest request) {
102120
ServletUriComponentsBuilder builder = initFromRequest(request);
@@ -107,6 +125,9 @@ public static ServletUriComponentsBuilder fromRequestUri(HttpServletRequest requ
107125
/**
108126
* Prepare a builder by copying the scheme, host, port, path, and
109127
* query string of an HttpServletRequest.
128+
*
129+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
130+
* and "X-Forwarded-*" headers if found. See class-level docs.
110131
*/
111132
public static ServletUriComponentsBuilder fromRequest(HttpServletRequest request) {
112133
ServletUriComponentsBuilder builder = initFromRequest(request);
@@ -155,6 +176,9 @@ private static String prependForwardedPrefix(HttpServletRequest request, String
155176
/**
156177
* Same as {@link #fromContextPath(HttpServletRequest)} except the
157178
* request is obtained through {@link RequestContextHolder}.
179+
*
180+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
181+
* and "X-Forwarded-*" headers if found. See class-level docs.
158182
*/
159183
public static ServletUriComponentsBuilder fromCurrentContextPath() {
160184
return fromContextPath(getCurrentRequest());
@@ -163,6 +187,9 @@ public static ServletUriComponentsBuilder fromCurrentContextPath() {
163187
/**
164188
* Same as {@link #fromServletMapping(HttpServletRequest)} except the
165189
* request is obtained through {@link RequestContextHolder}.
190+
*
191+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
192+
* and "X-Forwarded-*" headers if found. See class-level docs.
166193
*/
167194
public static ServletUriComponentsBuilder fromCurrentServletMapping() {
168195
return fromServletMapping(getCurrentRequest());
@@ -171,6 +198,9 @@ public static ServletUriComponentsBuilder fromCurrentServletMapping() {
171198
/**
172199
* Same as {@link #fromRequestUri(HttpServletRequest)} except the
173200
* request is obtained through {@link RequestContextHolder}.
201+
*
202+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
203+
* and "X-Forwarded-*" headers if found. See class-level docs.
174204
*/
175205
public static ServletUriComponentsBuilder fromCurrentRequestUri() {
176206
return fromRequestUri(getCurrentRequest());
@@ -179,6 +209,9 @@ public static ServletUriComponentsBuilder fromCurrentRequestUri() {
179209
/**
180210
* Same as {@link #fromRequest(HttpServletRequest)} except the
181211
* request is obtained through {@link RequestContextHolder}.
212+
*
213+
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
214+
* and "X-Forwarded-*" headers if found. See class-level docs.
182215
*/
183216
public static ServletUriComponentsBuilder fromCurrentRequest() {
184217
return fromRequest(getCurrentRequest());

src/docs/asciidoc/web/web-mvc.adoc

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

34383438

3439+
[[mvc-links-to-controllers-forwarded-headers]]
3440+
=== Working with "Forwarded" and "X-Forwarded-*" Headers
3441+
3442+
As a request goes through proxies such as load balancers the host, port, and
3443+
scheme may change presenting a challenge for applications that need to create links
3444+
to resources since the links should reflect the host, port, and scheme of the
3445+
original request as seen from a client perspective.
3446+
3447+
https://tools.ietf.org/html/rfc7239[RFC 7239] defines the "Forwarded" HTTP header
3448+
for proxies to use to provide information about the original request. There are also
3449+
other non-standard headers in use such as "X-Forwarded-Host", "X-Forwarded-Port",
3450+
and "X-Forwarded-Proto".
3451+
3452+
Both `ServletUriComponentsBuilder` and `MvcUriComponentsBuilder` detect, extract, and use
3453+
information from the "Forwarded" header, or from "X-Forwarded-Host", "X-Forwarded-Port",
3454+
and "X-Forwarded-Proto" if "Forwarded" is not present, so that the resulting links reflect
3455+
the original request.
3456+
3457+
The `ForwardedHeaderFilter` provides an alternative to do the same once and globally for
3458+
the entire application. The filter wraps the request in order to overlay host, port, and
3459+
scheme information and also "hides" any forwarded headers for subsequent processing.
3460+
3461+
Note that there are security considerations when using forwarded headers as explained
3462+
in Section 8 of RFC 7239. At the application level it is difficult to determine whether
3463+
forwarded headers can be trusted or not. This is why the network upstream should be
3464+
configured correctly to filter out untrusted forwarded headers from the outside.
3465+
3466+
Applications that don't have a proxy and don't need to use forwarded headers can
3467+
configure the `ForwardedHeaderFilter` to remove and ignore such headers.
3468+
3469+
3470+
34393471
[[mvc-links-to-controllers-from-views]]
34403472
=== Building URIs to Controllers and methods from views
34413473

0 commit comments

Comments
 (0)