rust 44 lines · 7 steps

Building HTTP redirect handlers in Axum

Three Axum handlers turn legacy URLs into clean permanent redirects using path, query, and URI extractors.

Explained by highlit
1use axum::{
2 extract::{Path, Query},
3 http::Uri,
4 response::{IntoResponse, Redirect},
5};
6use serde::Deserialize;
7 
8#[derive(Deserialize)]
9pub struct LegacyArticleParams {
10 id: u64,
11}
12 
13#[derive(Deserialize)]
14pub struct LegacyQuery {
15 #[serde(default)]
16 ref_source: Option<String>,
17}
18 
19pub async fn redirect_legacy_article(
20 Path(params): Path<LegacyArticleParams>,
21 Query(query): Query<LegacyQuery>,
22) -> impl IntoResponse {
23 let mut target = format!("/articles/{}", params.id);
24 if let Some(source) = query.ref_source {
25 target.push_str(&format!("?utm_source={}", source));
26 }
27 Redirect::permanent(&target)
28}
29 
30pub async fn redirect_trailing_slash(uri: Uri) -> impl IntoResponse {
31 let path = uri.path();
32 let trimmed = path.trim_end_matches('/');
33 let canonical = if trimmed.is_empty() { "/" } else { trimmed };
34 
35 match uri.query() {
36 Some(q) => Redirect::permanent(&format!("{}?{}", canonical, q)),
37 None => Redirect::permanent(canonical),
38 }
39}
40 
41pub async fn redirect_old_blog(Path(slug): Path<String>) -> impl IntoResponse {
42 let normalized = slug.to_lowercase().replace('_', "-");
43 Redirect::permanent(&format!("/posts/{}", normalized))
44}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Axum extractors like Path and Query deserialize request parts straight into typed structs for you.
  2. 2Permanent redirects (301) tell browsers and search engines the old URL has moved for good.
  3. 3Preserving query strings while rewriting paths keeps tracking params and canonical URLs intact.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building HTTP redirect handlers in Axum — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code