> ## 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.

# Redirect

> Redirect requests to different locations with appropriate HTTP status codes

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:

```rust theme={null}
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:

```rust theme={null}
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:

```rust theme={null}
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:

```rust theme={null}
use axum::response::Redirect;

async fn moved_permanently() -> Redirect {
    Redirect::permanent("/new-location")
}
```

## How it works

### Type definition

```rust theme={null}
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:

```rust theme={null}
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`.

<Note>
  If the location URI contains invalid characters (like newlines), the response will return `500 Internal Server Error`.
</Note>

## 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

<ResponseField name="Location" type="header">
  The URI to redirect to. Set from the parameter passed to the constructor.
</ResponseField>

<ResponseField name="status" type="StatusCode">
  The HTTP status code: 303 (See Other), 307 (Temporary Redirect), or 308 (Permanent Redirect)
</ResponseField>

## Common patterns

### Post-login redirect

```rust theme={null}
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

```rust theme={null}
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

```rust theme={null}
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

```rust theme={null}
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

```rust theme={null}
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:

```rust theme={null}
let redirect = Redirect::permanent("/new");
assert_eq!(redirect.status_code(), StatusCode::PERMANENT_REDIRECT);
```

### location()

Returns the redirect URI as a string slice:

```rust theme={null}
let redirect = Redirect::to("/dashboard");
assert_eq!(redirect.location(), "/dashboard");
```

## See also

* [Response tuples](/api/responses/tuples) for combining with headers
* [IntoResponse trait](/api/responses/into-response) for custom response types
* [StatusCode](/api/responses/status-code) for other status codes
