skip to content

Contrast RedirectTo and SetStatus. What does each do to the response and request flow?

level: middleimportance: should knowfreq 45%

answer

  1. SetStatus = override status, STILL forwards
  2. RedirectTo = 3xx + Location, short-circuits
  3. RedirectTo does NOT call downstream
  4. original-status-header-name preserves backend code
  5. RedirectTo URL is absolute

basics

~20 s

SetStatus just overrides the HTTP status code of the response the client gets, while still forwarding to the downstream. RedirectTo short-circuits the route: it returns a redirect (status + Location header) to the client and does not forward the request downstream.

solid answer

~40 s

SetStatus takes a status (e.g. `SetStatus=401`) and overrides the response status code returned to the client after the request has gone downstream — the downstream is still called, only the final status is changed. It can optionally preserve the original downstream status in a header via the `spring.cloud.gateway.set-status.original-status-header-name` property. RedirectTo takes a status and a URL (e.g. `RedirectTo=302, https://example.org`). It is a short-circuiting filter: the gateway sets the redirect status and the `Location` header and completes the exchange without proxying to the downstream URI at all. So the fundamental difference is flow: SetStatus is a post-style status override on an actually-forwarded request; RedirectTo replaces forwarding with an immediate redirect response. RedirectTo's status should be a 3xx for the Location header to be meaningful to browsers.

code

kotlin · 16 lines
kotlin
@Bean
fun routes(builder: RouteLocatorBuilder): RouteLocator =
    builder.routes()
        // Short-circuit: browser gets 302 + Location, backend never called
        .route("moved") { r ->
            r.path("/old/**")
                .filters { f -> f.redirect(302, "https://new.example.org") }
                .uri("no://op") // required but unused for RedirectTo
        }
        // Forwarded, but client always sees 401 regardless of backend status
        .route("mask-status") { r ->
            r.path("/secure/**")
                .filters { f -> f.setStatus(401) }
                .uri("http://secure-service:8080")
        }
        .build()

go deeper

for a junior

Knows SetStatus changes the code and RedirectTo sends a redirect.

for a middle

Explains that RedirectTo short-circuits while SetStatus still forwards, and the 3xx requirement.

for a senior

Mentions original-status-header-name and observability/masking concerns.

for a principal

Weighs redirect-at-gateway vs backend, includeRequestParams, and status-masking pitfalls for clients and monitoring.

Both are per-route GatewayFilter factories that affect the **response**, but they differ fundamentally in whether the request is still forwarded. **SetStatus.** One argument: the HTTP status to return, accepted as a numeric code (`401`) or an enum-style name (`UNAUTHORIZED`). ```yaml filters: - SetStatus=401 ``` Behavior: the request **is still routed to the downstream service**; when the response comes back, SetStatus **overrides the status code** the client ultimately sees. The body and other headers from downstream are otherwise passed through. Because it changes status after the exchange has been handled, it behaves like a post-phase override. There is a related global property, `spring.cloud.gateway.set-status.original-status-header-name`, that, when set, copies the *original* downstream status into a response header so the real backend status isn't lost. Use SetStatus to normalize/override the status the client observes (e.g. mask a backend code) while still actually calling the backend. **RedirectTo.** Two (optionally three) arguments: a **status** and a **URL** (and, in current versions, an optional `includeRequestParams` boolean). ```yaml filters: - RedirectTo=302, https://new.example.org ``` Behavior: this is a **short-circuiting** filter. The gateway sets the response status to the given code and sets the `Location` header to the given URL, then **completes the exchange without proxying to the route's downstream `uri`**. The client receives a redirect and follows it to the new location. The status should be a redirect code (301, 302, 307, 308) so the `Location` header is meaningful; using a non-3xx makes the Location semantically pointless for browsers. **Key contrasts.** - *Downstream call*: SetStatus still forwards; RedirectTo does not (it short-circuits). - *Location header*: RedirectTo sets it; SetStatus does not. - *Typical status*: SetStatus any code you want the client to see; RedirectTo a 3xx. - *Use case*: SetStatus = override/normalize response status of a real proxied call; RedirectTo = permanently/temporarily move a public URL, force HTTPS, or point old paths elsewhere without touching the backend. **Gotchas.** With RedirectTo you still typically must declare a `uri` on the route (config requires one) even though it is never actually called. RedirectTo's URL is absolute — it replaces the destination entirely. SetStatus overriding to a success code can mask real backend failures from clients, so use the original-status-header to keep observability. Neither modifies the response *body*; for that you'd need a body-modifying filter.

  • With RedirectTo, is the route's downstream uri ever contacted?
    No. RedirectTo short-circuits: it returns the redirect status and Location header and completes the exchange, so the downstream uri is not proxied to (though config may still require a uri).
  • How can you keep the real backend status when using SetStatus to override it?
    Set spring.cloud.gateway.set-status.original-status-header-name; the original downstream status is copied into that response header before the override is applied.

saying these in an interview costs you the question

  • Saying RedirectTo still forwards to the downstream and then redirects
  • Thinking SetStatus adds a Location header
  • Using a non-3xx status with RedirectTo and expecting browsers to redirect
  • Assuming SetStatus can change the response body

context