The Redirect response type creates redirect responses using the Location header with various redirect status codes (303, 307, 308).
Basic usage
Create a redirect using one of three constructors:
use axum::{response::Redirect, routing::get, Router};
let app = Router::new()
.route("/old", get(|| async { Redirect::permanent("/new") }))
.route("/new", get(|| async { "Hello!" }));
Methods
Redirect::to
Creates a 303 See Other redirect that instructs the client to change the method to GET:
use axum::response::Redirect;
async fn after_form_submit() -> Redirect {
// After POST form submission, redirect to GET
Redirect::to("/success")
}
This is useful after successful form submissions or file uploads when you don’t want the redirected page to observe the original request method and body.
Redirect::temporary
Creates a 307 Temporary Redirect that preserves the original HTTP method and body:
use axum::response::Redirect;
async fn maintenance_redirect() -> Redirect {
// Preserves the request method (POST stays POST)
Redirect::temporary("/maintenance")
}
Redirect::permanent
Creates a 308 Permanent Redirect for permanent URL changes:
use axum::response::Redirect;
async fn moved_permanently() -> Redirect {
Redirect::permanent("/new-location")
}
How it works
Type definition
pub struct Redirect {
status_code: StatusCode,
location: String,
}
See implementation in axum/src/response/redirect.rs:22-25.
IntoResponse implementation
The Redirect type sets the Location header with the specified status code:
impl IntoResponse for Redirect {
fn into_response(self) -> Response {
match HeaderValue::try_from(self.location) {
Ok(location) => (self.status_code, [(LOCATION, location)]).into_response(),
Err(error) => (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response(),
}
}
}
See implementation in axum/src/response/redirect.rs:87-94.
If the location URI contains invalid characters (like newlines), the response will return 500 Internal Server Error.
Status codes explained
303 See Other (Redirect::to)
- Behavior: Forces the client to use GET for the subsequent request
- Use case: After POST form submissions, file uploads, or any operation where you want to prevent resubmission
- Method change: YES - any method becomes GET
307 Temporary Redirect (Redirect::temporary)
- Behavior: Preserves the original HTTP method and body
- Use case: Temporary maintenance pages, A/B testing, or temporary URL changes
- Method change: NO - POST stays POST, GET stays GET
308 Permanent Redirect (Redirect::permanent)
- Behavior: Indicates permanent URL relocation while preserving method and body
- Use case: Permanent URL structure changes, domain migrations
- Method change: NO - method is preserved
Response fields
The URI to redirect to. Set from the parameter passed to the constructor.
The HTTP status code: 303 (See Other), 307 (Temporary Redirect), or 308 (Permanent Redirect)
Common patterns
Post-login redirect
use axum::{
extract::Form,
http::{header::SET_COOKIE, HeaderMap},
response::Redirect,
};
async fn login(Form(credentials): Form<Credentials>) -> (HeaderMap, Redirect) {
let session_cookie = create_session(&credentials).await;
let mut headers = HeaderMap::new();
headers.insert(
SET_COOKIE,
format!("session={session_cookie}; Path=/; HttpOnly").parse().unwrap(),
);
(headers, Redirect::to("/"))
}
Example from examples/oauth/src/main.rs:153-184.
OAuth callback redirect
use axum::response::Redirect;
async fn oauth_callback(/* ... */) -> Redirect {
// After successful OAuth, redirect back to home
Redirect::to("/")
}
Example from examples/oauth/src/main.rs:317.
Conditional redirects
use axum::response::Redirect;
async fn logout(session: Option<Session>) -> Redirect {
match session {
Some(s) => {
destroy_session(s).await;
Redirect::to("/")
}
None => Redirect::to("/"),
}
}
HTTPS redirect
use axum::{
extract::OriginalUri,
http::StatusCode,
response::Redirect,
};
async fn redirect_to_https(OriginalUri(uri): OriginalUri) -> Result<Redirect, StatusCode> {
let https_uri = format!("https://example.com{}", uri.path());
Ok(Redirect::permanent(https_uri))
}
Example pattern from examples/tls-rustls/src/main.rs.
Using in custom error responses
use axum::{
response::{IntoResponse, Redirect, Response},
};
struct AuthRedirect;
impl IntoResponse for AuthRedirect {
fn into_response(self) -> Response {
Redirect::temporary("/auth/discord").into_response()
}
}
Example from examples/oauth/src/main.rs:320-326.
Helper methods
status_code()
Returns the HTTP status code of the redirect:
let redirect = Redirect::permanent("/new");
assert_eq!(redirect.status_code(), StatusCode::PERMANENT_REDIRECT);
location()
Returns the redirect URI as a string slice:
let redirect = Redirect::to("/dashboard");
assert_eq!(redirect.location(), "/dashboard");
See also