diff --git a/main.go b/main.go
index 385a48eb4d3823d44c130debd427714593d2b141..fb7bc98e4f4c672ee54baa65070446c39e6bc7cb 100644
--- a/main.go
+++ b/main.go
@@ -15,6 +15,7 @@ import (
 	"os"
 	"path/filepath"
 	"time"
+	"path"
 )
 
 func writeBody(reader io.ReadCloser, path string) error {
@@ -272,14 +273,164 @@ func main() {
 		}
 	})
 
+	http.Handle("/i/", http.StripPrefix("/i/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
+		user := parseUser(r)
+
+		type ImageDetailData struct {
+			User   UserInfo
+			Image  Image
+			IsMine bool
+		}
+
+		imageId := r.URL
+		result, err := db.Query(`
+			SELECT
+				id,
+				owner,
+				coalesce(title,  ''),
+				coalesce(description, ''),
+        		coalesce(created_at, to_timestamp(0)),
+				coalesce(original_name, ''),
+				coalesce(type, '')
+			FROM images
+			WHERE id = $2
+			`, user.Id, imageId)
+		if err != nil {
+			panic(err)
+		}
+
+		var info Image
+		if result.Next() {
+			var owner string
+			err := result.Scan(&info.Id, &owner, &info.Title, &info.Description, &info.CreatedAt, &info.OriginalName, &info.MimeType)
+			if err != nil {
+				panic(err)
+			}
+
+			switch r.PostFormValue("action") {
+			case "update":
+				_, err = db.Exec(
+					"UPDATE images SET title = $1, description = $2 WHERE id = $3 AND owner = $4",
+					r.PostFormValue("title"),
+					r.PostFormValue("description"),
+					info.Id,
+					user.Id,
+				)
+				if err != nil {
+					panic(err)
+				}
+				return
+			case "delete":
+				_, err = db.Exec("DELETE FROM images WHERE id = $1 AND owner = $2", info.Id, user.Id)
+				if err != nil {
+					panic(err)
+				}
+				for _, definition := range config.Sizes {
+					os.Remove(path.Join(config.TargetFolder, fmt.Sprintf("%s%s", info.Id, definition.Suffix)))
+				}
+				return
+			default:
+				if err = returnResult(w, "image_detail.html", ImageDetailData{
+					user,
+					info,
+					owner == user.Id,
+				}); err != nil {
+					panic(err)
+				}
+				return
+			}
+		}
+
+		if err = returnResult(w, "image_detail.html", ImageDetailData{
+			user,
+			Image{},
+			false,
+		}); err != nil {
+			panic(err)
+		}
+	})))
+
 	http.HandleFunc("/me/images/", func(w http.ResponseWriter, r *http.Request) {
 		user := parseUser(r)
 
+		type ImageDetailData struct {
+			User  UserInfo
+			Image Image
+		}
+
 		type ImageListData struct {
 			User   UserInfo
 			Images []Image
 		}
 
+		dir, imageId := path.Split(r.URL.Path)
+		if dir == "/me/images/" {
+			result, err := db.Query(`
+			SELECT
+				id,
+				coalesce(title,  ''),
+				coalesce(description, ''),
+        		coalesce(created_at, to_timestamp(0)),
+				coalesce(original_name, ''),
+				coalesce(type, '')
+			FROM images
+			WHERE owner = $1
+			AND id = $2
+			`, user.Id, imageId)
+			if err != nil {
+				panic(err)
+			}
+
+			var info Image
+			if result.Next() {
+				err := result.Scan(&info.Id, &info.Title, &info.Description, &info.CreatedAt, &info.OriginalName, &info.MimeType)
+				if err != nil {
+					panic(err)
+				}
+
+				switch r.Method {
+				case "GET":
+					if err = returnResult(w, "me_images_info.html", ImageDetailData{
+						user,
+						info,
+					}); err != nil {
+						panic(err)
+					}
+					return
+				case "POST":
+					_, err = db.Exec(
+						"UPDATE images SET title = $1, description = $2 WHERE id = $3 AND owner = $4",
+						r.PostFormValue("title"),
+						r.PostFormValue("description"),
+						info.Id,
+						user.Id,
+					)
+					if err != nil {
+						panic(err)
+					}
+					return
+				case "DELETE":
+					_, err = db.Exec("DELETE FROM images WHERE id = $1 AND owner = $2", info.Id, user.Id)
+					if err != nil {
+						panic(err)
+					}
+					for _, definition := range config.Sizes {
+						os.Remove(path.Join(config.TargetFolder, fmt.Sprintf("%s%s", info.Id, definition.Suffix)))
+					}
+					return
+				}
+			}
+
+			if err = returnResult(w, "me_images_info.html", ImageDetailData{
+				user,
+				Image{},
+			}); err != nil {
+				panic(err)
+			}
+			return
+		}
+
 		result, err := db.Query(`
 			SELECT
 				id,
diff --git a/templates/image_detail.html b/templates/image_detail.html
new file mode 100644
index 0000000000000000000000000000000000000000..c153e5388ef15455e974e46e9d80d060d2eea573
--- /dev/null
+++ b/templates/image_detail.html
@@ -0,0 +1,30 @@
+{{ if .Image.Id }}
+{{define "title"}}{{.Title}} | i.k8r{{end}}
+{{define "content"}}
+<form class="detail" method="post">
+    <input type="hidden" name="action" value="update">
+{{if .IsMine}}
+    <input name="title" class="title" value="{{.Image.Title}}">
+{{else}}
+    <h2 class="title">{{.Image.Title}}</h2>
+{{end}}
+    <div class="image">
+        <img src="/{{.Image.Id}}">
+    </div>
+{{if .IsMine}}
+    <textarea name="description" class="description">{{.Image.Description}}</textarea>
+{{else}}
+    <div class="description">{{.Image.Description}}</div>
+{{end}}
+{{if .IsMine}}
+    <div class="controls">
+        <form method="post" class="delete-form">
+            <input type="hidden" name="action" value="delete">
+            <input type="submit" value="Delete">
+        </form>
+        <input type="submit" value="Save">
+    </div>
+{{end}}
+</form>
+{{end}}
+{{end}}
\ No newline at end of file
diff --git a/templates/me_images.html b/templates/me_images.html
index 4ffc98ebdbb5493d8c9a4d610f15b1afcd1130fe..0dbf5c53ca15efd7b557cecd5ed33a8fda074e74 100644
--- a/templates/me_images.html
+++ b/templates/me_images.html
@@ -2,14 +2,10 @@
 {{define "content"}}
 <div class="images">
 {{range .Images}}
-    <a class="image" href="/{{.Id}}">
+    <a class="image" href="/i/{{.Id}}">
         <div class="image-container">
             <img src="/{{.Id}}t" >
         </div>
-        <form action="/me/images/delete" method="post" class="delete-form">
-            <input type="hidden" name="id" value="{{.Id}}">
-            <input type="submit" value="Delete">
-        </form>
         <div class="info">
             <p>{{.OriginalName}}</p>
             <p><time>{{.CreatedAt.Format "2006-01-02 15:04"}}</time></p>