diff --git a/backend/src/main.rs b/backend/src/main.rs
index 4803cd5..da280fd 100644
--- a/backend/src/main.rs
+++ b/backend/src/main.rs
@@ -1,17 +1,15 @@
 use rocket::fs::NamedFile;
 use rocket::http::Method;
 use rocket::http::Status;
+use rocket::response::Redirect;
 use rocket::response::{Responder, status};
 use rocket::serde::{Deserialize, json::Json};
+use rocket::uri;
 use rocket::{self, get, launch, post, routes};
 use rocket_cors::{AllowedOrigins, CorsOptions};
 use std::env;
 use std::path::Path;
-
-#[get("/")]
-fn index() -> &'static str {
-    "Hello, world!"
-}
+use std::path::PathBuf;
 
 #[derive(Deserialize)]
 #[serde(crate = "rocket::serde")]
@@ -202,6 +200,22 @@ async fn generate(request: Json<GenerationRequest<'_>>) -> Result<NamedFile, Gen
     }
 }
 
+#[get("/")]
+fn index_redirect() -> Redirect {
+    Redirect::permanent(uri!("/frontend").to_string() + "/")
+}
+
+#[get("/frontend/<path..>")]
+async fn frontend(path: PathBuf) -> Option<NamedFile> {
+    let frontend_base = env::var("FRONTEND_BASE").unwrap_or("../frontend/dist/".into());
+    let frontend_path = Path::new(&frontend_base).join(path);
+    let frontend_index_path = Path::new(&frontend_base).join("index.html");
+    NamedFile::open(frontend_path)
+        .await
+        .or(NamedFile::open(frontend_index_path).await)
+        .ok()
+}
+
 #[launch]
 fn rocket() -> _ {
     let cors = CorsOptions::default()
@@ -217,7 +231,8 @@ fn rocket() -> _ {
     rocket::build()
         .mount(
             "/api/v1",
-            routes![index, list_directories, list_directory, get_file, generate],
+            routes![list_directories, list_directory, get_file, generate],
         )
         .attach(cors.to_cors().unwrap())
+        .mount("/", routes![index_redirect, frontend])
 }