diff --git a/CHANGELOG.md b/CHANGELOG.md
index a509f43987d0dcbd8a7412b97599b2f84b7b2832..780d867bd7549b0d9bc5866d19a6786bed2ba90d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
 - Fix: モバイル表示のときページ下部がナビゲーションバーに隠れる問題を修正
 - Fix: 一部モーダルダイアログでスクロールできない問題を修正
 - Fix: Selecting all emojis in Custom emoji is impossible
+- Fix: PhotoSwipeによるメモリリークの修正
 
 ### Server
 - Fix: APIのオフセットが壊れていたせいで「もっと見る」でもっと見れない問題を修正
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index 661c0a98776f71f0db20e5c7e4e848bb8a65b185..0cdccfb1697fd6b4382a8dd34721fe8ff531374b 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -58,7 +58,7 @@ async function getClientWidthWithCache(targetEl: HTMLElement, containerEl: HTMLE
 </script>
 
 <script lang="ts" setup>
-import { onMounted, shallowRef } from 'vue';
+import { onMounted, onUnmounted, shallowRef } from 'vue';
 import * as misskey from 'misskey-js';
 import PhotoSwipeLightbox from 'photoswipe/lightbox';
 import PhotoSwipe from 'photoswipe';
@@ -82,12 +82,19 @@ const gallery = shallowRef<HTMLDivElement>();
 const pswpZIndex = os.claimZIndex('middle');
 document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
 const count = $computed(() => props.mediaList.filter(media => previewable(media)).length);
+let lightbox: PhotoSwipeLightbox | null;
+
+const popstateHandler = (): void => {
+	if (lightbox.pswp && lightbox.pswp.isOpen === true) {
+		lightbox.pswp.close();
+	}
+};
 
 /**
  * アスペクト比をmediaListWithOneImageAppearanceに基づいていい感じに調整する
  * aspect-ratioではなくheightを使う
  */
- async function calcAspectRatio() {
+async function calcAspectRatio() {
 	if (!gallery.value || !root.value) return;
 
 	let img = props.mediaList[0];
@@ -137,7 +144,7 @@ const count = $computed(() => props.mediaList.filter(media => previewable(media)
 onMounted(() => {
 	calcAspectRatio();
 
-	const lightbox = new PhotoSwipeLightbox({
+	lightbox = new PhotoSwipeLightbox({
 		dataSource: props.mediaList
 			.filter(media => {
 				if (media.type === 'image/svg+xml') return true; // svgのwebpublicはpngなのでtrue
@@ -221,12 +228,7 @@ onMounted(() => {
 
 	lightbox.init();
 
-	window.addEventListener('popstate', () => {
-		if (lightbox.pswp && lightbox.pswp.isOpen === true) {
-			lightbox.pswp.close();
-			return;
-		}
-	});
+	window.addEventListener('popstate', popstateHandler);
 
 	lightbox.on('beforeOpen', () => {
 		history.pushState(null, '', '#pswp');
@@ -239,6 +241,12 @@ onMounted(() => {
 	});
 });
 
+onUnmounted(() => {
+	window.removeEventListener('popstate', popstateHandler);
+	lightbox?.destroy();
+	lightbox = null;
+});
+
 const previewable = (file: misskey.entities.DriveFile): boolean => {
 	if (file.type === 'image/svg+xml') return true; // svgのwebpublic/thumbnailはpngなのでtrue
 	// FILE_TYPE_BROWSERSAFEに適合しないものはブラウザで表示するのに不適切