diff --git a/README.md b/README.md
index 69dd244..6a64f3b 100644
--- a/README.md
+++ b/README.md
@@ -253,3 +253,7 @@ enabled over its HTTP interface you _must_ specify the `-L` flag to follow these
 redirects or else your request will terminate early with an empty response. We
 recommend the use of the `-L` flag in all deployments regardless of current
 HTTPS status to avoid accidental outages should it be enabled in the future.
+
+## My Links
+
+Navigate to `http://go/.mine` to view all the links you have created. This page filters the links to display only those owned by your account, providing a personalized view of your shortlinks.
\ No newline at end of file
diff --git a/db.go b/db.go
index 5e69fff..7a461d6 100644
--- a/db.go
+++ b/db.go
@@ -90,6 +90,31 @@ func (s *SQLiteDB) LoadAll() ([]*Link, error) {
 	return links, rows.Err()
 }
 
+// LoadByOwner retrieves all links owned by the specified user.
+func (s *SQLiteDB) LoadByOwner(owner string) ([]*Link, error) {
+	s.mu.RLock()
+	defer s.mu.RUnlock()
+
+	var links []*Link
+	rows, err := s.db.Query("SELECT Short, Long, Created, LastEdit, Owner FROM Links WHERE Owner = ?", owner)
+	if err != nil {
+		return nil, err
+	}
+	for rows.Next() {
+		link := new(Link)
+		var created, lastEdit int64
+		err := rows.Scan(&link.Short, &link.Long, &created, &lastEdit, &link.Owner)
+		if err != nil {
+			return nil, err
+		}
+		link.Created = time.Unix(created, 0).UTC()
+		link.LastEdit = time.Unix(lastEdit, 0).UTC()
+		links = append(links, link)
+	}
+	return links, rows.Err()
+}
+
+
 // Load returns a Link by its short name.
 //
 // It returns fs.ErrNotExist if the link does not exist.
@@ -217,3 +242,5 @@ func (s *SQLiteDB) DeleteStats(short string) error {
 	}
 	return nil
 }
+
+
diff --git a/golink.go b/golink.go
index 151b3d2..83fb750 100644
--- a/golink.go
+++ b/golink.go
@@ -262,6 +262,9 @@ var (
 
 	// opensearchTmpl is the template used by the http://go/.opensearch page
 	opensearchTmpl *template.Template
+
+	// mineTmpl is the template used by the http://go/.mine page
+	mineTmpl *template.Template
 )
 
 type visitData struct {
@@ -294,6 +297,7 @@ func init() {
 	allTmpl = newTemplate("base.html", "all.html")
 	deleteTmpl = newTemplate("base.html", "delete.html")
 	opensearchTmpl = newTemplate("opensearch.xml")
+	mineTmpl = newTemplate("base.html", "mine.html")
 
 	b := make([]byte, 24)
 	rand.Read(b)
@@ -498,6 +502,38 @@ func serveAll(w http.ResponseWriter, _ *http.Request) {
 	allTmpl.Execute(w, links)
 }
 
+func serveMine(w http.ResponseWriter, r *http.Request) {
+	cu, err := currentUser(r)
+	if err != nil {
+		http.Error(w, "Failed to retrieve current user", http.StatusInternalServerError)
+		return
+	}
+
+	// Flush stats before loading links
+	if err := flushStats(); err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+	// Load links owned by the current user
+	links, err := db.LoadByOwner(cu.login)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+	// Return JSON if the client doesn't accept HTML
+	if !acceptHTML(r) {
+		w.Header().Set("Content-Type", "application/json")
+		if err := json.NewEncoder(w).Encode(links); err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		}
+		return
+	}
+
+	mineTmpl.Execute(w, links)
+}
+
 func serveHelp(w http.ResponseWriter, _ *http.Request) {
 	helpTmpl.Execute(w, nil)
 }
@@ -518,6 +554,16 @@ func serveGo(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	// Route the user-specific link list /.mine endpoint
+	if r.URL.Path == "/.mine" {
+		if r.Method != "GET" {
+			http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
+			return
+		}
+		serveMine(w, r)
+		return
+	}
+
 	short, remainder, _ := strings.Cut(strings.TrimPrefix(r.URL.Path, "/"), "/")
 
 	// redirect {name}+ links to /.detail/{name}
@@ -1031,3 +1077,4 @@ func isRequestAuthorized(r *http.Request, u user, short string) bool {
 
 	return xsrftoken.Valid(r.PostFormValue("xsrf"), xsrfKey, u.login, short)
 }
+
diff --git a/golink_test.go b/golink_test.go
index 12c0263..be86b75 100644
--- a/golink_test.go
+++ b/golink_test.go
@@ -4,6 +4,7 @@
 package golink
 
 import (
+	"encoding/json"
 	"errors"
 	"net/http"
 	"net/http/httptest"
@@ -637,3 +638,87 @@ func TestNoHSTSShortDomain(t *testing.T) {
 		})
 	}
 }
+
+func TestServeMine(t *testing.T) {
+	var err error
+	db, err = NewSQLiteDB(":memory:")
+	if err != nil {
+		t.Fatal(err)
+	}
+	
+	// Seed the database with links - update Owner to match login format
+	db.Save(&Link{Short: "link1", Long: "http://example.com/1", Owner: "user1@example.com"})
+	db.Save(&Link{Short: "link2", Long: "http://example.com/2", Owner: "user2@example.com"})
+	db.Save(&Link{Short: "link3", Long: "http://example.com/3", Owner: "user1@example.com"})
+
+	tests := []struct {
+		name        string
+		currentUser func(*http.Request) (user, error)
+		wantLinks   []*Link
+		wantStatus  int
+	}{
+		{
+			name: "User with links",
+			currentUser: func(*http.Request) (user, error) {
+				return user{login: "user1@example.com"}, nil
+			},
+			wantLinks: []*Link{
+				{Short: "link1", Long: "http://example.com/1", Owner: "user1@example.com"},
+				{Short: "link3", Long: "http://example.com/3", Owner: "user1@example.com"},
+			},
+			wantStatus: http.StatusOK,
+		},
+		{
+			name: "User with no links",
+			currentUser: func(*http.Request) (user, error) {
+				return user{login: "user3@example.com"}, nil
+			},
+			wantLinks:  []*Link{},
+			wantStatus: http.StatusOK,
+		},
+		{
+			name: "Failed to retrieve user",
+			currentUser: func(*http.Request) (user, error) {
+				return user{}, errors.New("authentication failed")
+			},
+			wantLinks:  nil,
+			wantStatus: http.StatusInternalServerError,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if tt.currentUser != nil {
+				oldCurrentUser := currentUser
+				currentUser = tt.currentUser
+				t.Cleanup(func() {
+					currentUser = oldCurrentUser
+				})
+			}
+
+			r := httptest.NewRequest("GET", "/.mine", nil)
+			w := httptest.NewRecorder()
+			serveMine(w, r)
+
+			if w.Code != tt.wantStatus {
+				t.Errorf("serveMine() = %d; want %d", w.Code, tt.wantStatus)
+			}
+
+			if tt.wantStatus == http.StatusOK {
+				var gotLinks []*Link
+				err := json.NewDecoder(w.Body).Decode(&gotLinks)
+				if err != nil {
+					t.Fatalf("Failed to decode response: %v", err)
+				}
+				if len(gotLinks) != len(tt.wantLinks) {
+					t.Errorf("Number of links = %d; want %d", len(gotLinks), len(tt.wantLinks))
+				}
+				for i, link := range gotLinks {
+					if link.Short != tt.wantLinks[i].Short || link.Owner != tt.wantLinks[i].Owner {
+						t.Errorf("Link %d = %+v; want %+v", i, link, tt.wantLinks[i])
+					}
+				}
+			}
+		})
+	}
+}
diff --git a/schema.sql b/schema.sql
index ac6dd04..344c476 100644
--- a/schema.sql
+++ b/schema.sql
@@ -7,6 +7,8 @@ CREATE TABLE IF NOT EXISTS Links (
 	Owner	 TEXT    NOT NULL DEFAULT ""
 );
 
+CREATE INDEX IF NOT EXISTS idx_owner ON Links (Owner);
+
 CREATE TABLE IF NOT EXISTS Stats (
 	ID       TEXT    NOT NULL DEFAULT "",
 	Created  INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), -- unix seconds
diff --git a/tmpl/home.html b/tmpl/home.html
index 0e146be..6138838 100644
--- a/tmpl/home.html
+++ b/tmpl/home.html
@@ -40,4 +40,5 @@ <h2 class="text-xl font-bold pt-6 pb-2">Popular Links</h2>
       </tbody>
     </table>
     <p class="my-2 text-sm"><a class="text-blue-600 hover:underline" href="/.all">See all links.</a></p>
+    <p class="my-2 text-sm"><a class="text-blue-600 hover:underline" href="/.mine">See all your links.</a></p>
 {{ end }}
diff --git a/tmpl/mine.html b/tmpl/mine.html
new file mode 100644
index 0000000..d309b66
--- /dev/null
+++ b/tmpl/mine.html
@@ -0,0 +1,31 @@
+{{ define "main" }}
+    <h2 class="text-xl font-bold pt-6 pb-2">My Links ({{ len . }} total)</h2>
+    <table class="table-auto w-full max-w-screen-lg">
+      <thead class="border-b border-gray-200 uppercase text-xs text-gray-500 text-left">
+        <tr class="flex">
+          <th class="flex-1 p-2">Link</th>
+          <th class="hidden md:block w-60 truncate p-2">Owner</th>
+          <th class="hidden md:block w-32 p-2">Last Edited</th>
+        </tr>
+      </thead>
+      <tbody>
+      {{ range . }}
+        <tr class="flex hover:bg-gray-100 group border-b border-gray-200">
+          <td class="flex-1 p-2">
+            <div class="flex">
+              <a class="flex-1 hover:text-blue-500 hover:underline" href="/{{ .Short }}">{{go}}/{{ .Short }}</a>
+              <a class="flex items-center px-2 invisible group-hover:visible" title="Link Details" href="/.detail/{{ .Short }}">
+                <svg class="hover:fill-blue-500" xmlns="http://www.w3.org/2000/svg" height="1.3em" viewBox="0 0 24 24" width="1.3em" fill="#000000" stroke-width="2"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg>
+              </a>
+            </div>
+            <p class="text-sm leading-normal text-gray-500 group-hover:text-gray-700 max-w-[75vw] md:max-w-[40vw] truncate">{{ .Long }}</p>
+            <p class="md:hidden text-sm leading-normal text-gray-700"><span class="text-gray-500 inline-block w-20">Owner</span> {{ .Owner }}</p>
+            <p class="md:hidden text-sm leading-normal text-gray-700"><span class="text-gray-500 inline-block w-20">Last Edited</span> {{ .LastEdit.Format "Jan 2, 2006" }}</p>
+          </td>
+          <td class="hidden md:block w-60 truncate p-2">{{ .Owner }}</td>
+          <td class="hidden md:block w-32 p-2">{{ .LastEdit.Format "Jan 2, 2006" }}</td>
+        </tr>
+      {{ end }}
+      </tbody>
+    </table>
+{{ end }}
\ No newline at end of file