A '404 Not Found' error is not just good UX (user experience) for your visitors but also for search engine bots. Usually, the average visitor sees it as just a normal page but, technically, in the background it is supposed to emit a header that tells search engines that the resource does not exist.
Previously, while building this blog using Axum I had defaulted to this fallback:
///routes.rs
/// Create and configure the application router
pub fn create_router(state: AppState) -> Router {
//...code stripped for brevity
Router::new()
// Public routes
.route("/", get(home::index_handler))
.route("/some-other-route", get(home::other_handler))
.layer(cors)//Set CORS protection guards
.with_state(state)
.fallback(axum::routing::get(|| async { "404 - Page not found" }))
}
Unfortunately, this is BAD PRACTICE. While the user will get the '404 - Page not found' error, a '200 OK' header is served at the broswer level and also to search engine bots which is deceptive. A '200 OK' response simply means that the route/resource accessed is available and exists yet in reality it does NOT exist. So what to do? Simple. Return a proper Status Code '404' on the fallback. This is easy to do. The first approach would be to wrap the status code in the 'fallback' call:
///routes.rs
use axum::{
routing::{get},
Router,
http::{StatusCode},
};
/// Create and configure the application router
pub fn create_router(state: AppState) -> Router {
//...code stripped for brevity
Router::new()
// Public routes
.route("/", get(home::index_handler))
.route("/some-other-route", get(home::other_handler))
.layer(cors)//Set CORS protection guards
.with_state(state)
.fallback(|| async {
(StatusCode::NOT_FOUND, "404 - Page not found")
})
}
Second approach is to create a function that returns the '404' error that would then be called within the 'fallback' call:
///routes.rs
use axum::{
routing::{get},
Router,
http::{StatusCode},
};
async fn handler_404() -> (StatusCode, &'static str) {
(StatusCode::NOT_FOUND, "404 - Page not found")
}
/// Create and configure the application router
pub fn create_router(state: AppState) -> Router {
//...code stripped for brevity
Router::new()
// Public routes
.route("/", get(home::index_handler))
.route("/some-other-route", get(home::other_handler))
.layer(cors)//Set CORS protection guards
.with_state(state)
.fallback(handler_404);
}
Either way works as expected. Always, remember to return a proper '404 Error' header for nonexistent resources instead of merely printing the error to the end user.
WORDCOUNT: 360 words.