diff --git a/qlfile b/qlfile
index cd41e75..b5ae7d4 100644
--- a/qlfile
+++ b/qlfile
@@ -3,3 +3,4 @@ ql fiveam
 ql cl-jingle
 git piccolo https://github.com/skyizwhite/piccolo.git
 git ningle-fbr https://github.com/skyizwhite/ningle-fbr.git
+ql cl-ppcre
diff --git a/qlfile.lock b/qlfile.lock
index d7ab4a7..3be1b4b 100644
--- a/qlfile.lock
+++ b/qlfile.lock
@@ -22,3 +22,7 @@
  (:class qlot/source/git:source-git
   :initargs (:remote-url "https://github.com/skyizwhite/ningle-fbr.git")
   :version "git-438030b0b89dc706c37932616e6bf82d0416ea26"))
+("cl-ppcre" .
+ (:class qlot/source/ql:source-ql
+  :initargs (:%version :latest)
+  :version "ql-2023-10-21"))
diff --git a/src/app.lisp b/src/app.lisp
index 75e37eb..2a98582 100644
--- a/src/app.lisp
+++ b/src/app.lisp
@@ -6,7 +6,7 @@
   (:local-nicknames (#:pi #:piccolo))
   (:local-nicknames (#:view #:hp/view))
   (:local-nicknames (#:cmp #:hp/components/**/*))
-  (:local-nicknames (#:mw #:hp/middleware))
+  (:local-nicknames (#:mw #:hp/middlewares/*))
   (:export #:start
            #:stop
            #:update))
@@ -21,18 +21,26 @@
                :title "404 Not Found"
                :description "お探しのページは見つかりませんでした。"))
 
-(defun update ()
-  (jg:clear-middlewares *app*)
-  (jg:install-middleware *app* mw:*public-files*)
-  (jg:static-path *app* "/scripts/" "src/scripts/")
-  (jg:static-path *app* "/styles/" "src/styles/")
-  (fbr:assign-routes *app*
-                     :system "hp"
-                     :directory "src/routes"))
-(update)
-
 (defun start ()
   (jg:start *app*))
 
 (defun stop ()
   (jg:stop *app*))
+
+(defun setup ()
+  (jg:clear-middlewares *app*)
+  (jg:clear-routing-rules *app*)
+  (fbr:assign-routes *app*
+                     :system "hp"
+                     :directory "src/routes")
+  (jg:static-path *app* "/scripts/" "src/scripts/")
+  (jg:static-path *app* "/styles/" "src/styles/")
+  (jg:install-middleware *app* mw:*public-files*)
+  (jg:install-middleware *app* mw:*normalize-path*))
+
+(defun update ()
+  (stop)
+  (setup)
+  (start))
+
+(setup)
diff --git a/src/middlewares/normalize-path.lisp b/src/middlewares/normalize-path.lisp
new file mode 100644
index 0000000..fa46502
--- /dev/null
+++ b/src/middlewares/normalize-path.lisp
@@ -0,0 +1,19 @@
+(defpackage #:hp/middlewares/normalize-path
+  (:use #:cl)
+  (:local-nicknames (#:re #:cl-ppcre))
+  (:export #:*normalize-path*))
+(in-package #:hp/middlewares/normalize-path)
+
+(defun has-trailing-slash-p (path)
+  (and (not (string= path "/")) (re:scan "\/$" path)))
+
+(defun remove-trailing-slash (path)
+  (re:regex-replace "\/$" path ""))
+
+(defparameter *normalize-path*
+  (lambda (app)
+    (lambda (env)
+      (let ((path (getf env :request-uri)))
+        (if (has-trailing-slash-p path)
+            `(308 (:location ,(remove-trailing-slash path)))
+            (funcall app env))))))
diff --git a/src/middleware.lisp b/src/middlewares/public-files.lisp
similarity index 85%
rename from src/middleware.lisp
rename to src/middlewares/public-files.lisp
index 6f0755f..aa2d612 100644
--- a/src/middleware.lisp
+++ b/src/middlewares/public-files.lisp
@@ -1,10 +1,9 @@
-(defpackage #:hp/middleware
+(defpackage #:hp/middlewares/public-files
   (:use #:cl)
-  (:local-nicknames (#:jg #:jingle))
   (:import-from #:lack.middleware.static
                 #:*lack-middleware-static*)
   (:export #:*public-files*))
-(in-package #:hp/middleware)
+(in-package #:hp/middlewares/public-files)
 
 (defun exist-public-file-p (path)
   (let ((pathname (probe-file (concatenate 'string "public" path))))