Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tokio-rs/axum/llms.txt

Use this file to discover all available pages before exploring further.

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

Location
header
The URI to redirect to. Set from the parameter passed to the constructor.
status
StatusCode
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

Build docs developers (and LLMs) love