feat: enforce canonical URLs by redirecting trailing slashes

This commit is contained in:
2026-01-13 14:17:42 -06:00
parent 99f9b5e303
commit eabe866d31
2 changed files with 12 additions and 0 deletions
+10
View File
@@ -22,6 +22,16 @@ pub async fn isr_handler(State(state): State<Arc<AppState>>, req: Request) -> Re
let path = uri.path(); let path = uri.path();
let query = uri.query(); let query = uri.query();
// Redirect trailing slashes to non-trailing (except root)
if path.len() > 1 && path.ends_with('/') {
let normalized = path.trim_end_matches('/');
let redirect_uri = match query {
Some(q) => format!("{normalized}?{q}"),
None => normalized.to_string(),
};
return axum::response::Redirect::permanent(&redirect_uri).into_response();
}
if method != axum::http::Method::GET && method != axum::http::Method::HEAD { if method != axum::http::Method::GET && method != axum::http::Method::HEAD {
tracing::warn!(method = %method, path = %path, "Non-GET/HEAD request to non-API route"); tracing::warn!(method = %method, path = %path, "Non-GET/HEAD request to non-API route");
+2
View File
@@ -4,6 +4,8 @@ import { apiFetch } from "$lib/api.server";
import type { SiteSettings } from "$lib/admin-types"; import type { SiteSettings } from "$lib/admin-types";
import { building } from "$app/environment"; import { building } from "$app/environment";
export const trailingSlash = "never";
const DEFAULT_SETTINGS: SiteSettings = { const DEFAULT_SETTINGS: SiteSettings = {
identity: { identity: {
siteTitle: "xevion.dev", siteTitle: "xevion.dev",