diff --git a/CHANGELOG.md b/CHANGELOG.md
index 77df0d292e..c9bda35348 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,15 @@
 
 You should also include the user name that made the change.
 -->
+## 13.5.4 (2023/02/09)
+
+### Improvements
+- Server: UIのHTML(ノートなどの特別なページを除く)のキャッシュ時間を15秒から30秒に
+- i/notificationsのレートリミットを緩和
+
+### Bugfixes
+- fix(client): validate url to improve security
+- fix(client): dateの初期値が正常に入らない時がある
 
 ## 13.5.3 (2023/02/09)
 
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 2464fb1240..f3cd85e6a0 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -129,6 +129,7 @@ unblockConfirm: "¿Quiere dejar de bloquear esta cuenta?"
 suspendConfirm: "¿Quiere suspender esta cuenta?"
 unsuspendConfirm: "¿Quiere dejar de suspender esta cuenta?"
 selectList: "Seleccione una lista"
+selectChannel: "Seleccionar canal"
 selectAntenna: "Seleccionar antena"
 selectWidget: "Seleccionar widget"
 editWidgets: "Editar widgets"
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 8e8fddfb89..09069e7801 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -940,6 +940,8 @@ cannotPerformTemporaryDescription: "操作回数が制限を超過するため
 preset: "プリセット"
 selectFromPresets: "プリセットから選択"
 achievements: "実績"
+gotInvalidResponseError: "サーバーの応答が無効です"
+gotInvalidResponseErrorDescription: "サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから再度お試しください。"
 
 _achievements:
   earnedAt: "獲得日時"
diff --git a/package.json b/package.json
index cbf67dc0da..da23fab135 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "13.5.3",
+	"version": "13.5.4",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts
index 13de3382dd..706e0d2089 100644
--- a/packages/backend/src/server/api/endpoints/i/notifications.ts
+++ b/packages/backend/src/server/api/endpoints/i/notifications.ts
@@ -15,8 +15,8 @@ export const meta = {
 	requireCredential: true,
 
 	limit: {
-		duration: 60000,
-		max: 15,
+		duration: 30000,
+		max: 30,
 	},
 
 	kind: 'read:notifications',
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index 1fa8950d75..c69ee33ea3 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -337,7 +337,7 @@ export class ClientServerService {
 
 		const renderBase = async (reply: FastifyReply) => {
 			const meta = await this.metaService.fetch();
-			reply.header('Cache-Control', 'public, max-age=15');
+			reply.header('Cache-Control', 'public, max-age=30');
 			return await reply.view('base', {
 				img: meta.bannerUrl,
 				title: meta.name ?? 'Misskey',
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index d05901baec..8d6897c46d 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -35,7 +35,8 @@ html
 		link(rel='prefetch' href='https://xn--931a.moe/assets/info.jpg')
 		link(rel='prefetch' href='https://xn--931a.moe/assets/not-found.jpg')
 		link(rel='prefetch' href='https://xn--931a.moe/assets/error.jpg')
-		link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css')
+		//- https://github.com/misskey-dev/misskey/issues/9842
+		link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.2.0')
 		link(rel='modulepreload' href=`/vite/${clientEntry.file}`)
 
 		if !config.clientManifestExists
diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue
index 5c5151fce6..e3f68caa9b 100644
--- a/packages/frontend/src/components/MkInput.vue
+++ b/packages/frontend/src/components/MkInput.vue
@@ -42,7 +42,7 @@ import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	modelValue: string | number;
-	type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search';
+	type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local';
 	required?: boolean;
 	readonly?: boolean;
 	disabled?: boolean;
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 01f8244060..52469b6d04 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -35,6 +35,9 @@ export const apiWithDialog = ((
 		} else if (err.code.startsWith('TOO_MANY')) {
 			title = i18n.ts.youCannotCreateAnymore;
 			text = `${i18n.ts.error}: ${err.id}`;
+		} else if (err.message.startsWith('Unexpected token')) {
+			title = i18n.ts.gotInvalidResponseError;
+			text = i18n.ts.gotInvalidResponseErrorDescription;
 		}
 		alert({
 			type: 'error',
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 6d88feceaf..f8e9780714 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -73,7 +73,13 @@
 				</FormSection>
 				<FormSection>
 					<template #label><Mfm text="$[jelly ❤]"/> {{ i18n.ts._aboutMisskey.patrons }}</template>
-					<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); grid-gap: 12px;">
+					<div :class="$style.patronsWithIcon">
+						<div v-for="patron in patronsWithIcon" :class="$style.patronWithIcon">
+							<img :src="patron.icon" :class="$style.patronIcon">
+							<span :class="$style.patronName">{{ patron.name }}</span>
+						</div>
+					</div>
+					<div style="margin-top: 16px; display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); grid-gap: 12px;">
 						<div v-for="patron in patrons" :key="patron">{{ patron }}</div>
 					</div>
 					<p>{{ i18n.ts._aboutMisskey.morePatrons }}</p>
@@ -99,6 +105,14 @@ import { definePageMetadata } from '@/scripts/page-metadata';
 import { claimAchievement, claimedAchievements } from '@/scripts/achievements';
 import { $i } from '@/account';
 
+const patronsWithIcon = [{
+	name: 'カイヤン',
+	icon: 'https://misskey-hub.net/patrons/a2820716883e408cb87773e377ce7c8d.jpg',
+}, {
+	name: 'だれかさん',
+	icon: 'https://misskey-hub.net/patrons/f7409b5e5a88477a9b9d740c408de125.jpg',
+}];
+
 const patrons = [
 	'まっちゃとーにゅ',
 	'mametsuko',
@@ -352,4 +366,27 @@ definePageMetadata({
 .contributorUsername {
 	margin-left: 12px;
 }
+
+.patronsWithIcon {
+	display: grid;
+	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+	grid-gap: 12px;
+}
+
+.patronWithIcon {
+	display: flex;
+	align-items: center;
+	padding: 12px;
+	background: var(--buttonBg);
+	border-radius: 6px;
+}
+
+.patronIcon {
+	width: 24px;
+	border-radius: 100%;
+}
+
+.patronName {
+	margin-left: 12px;
+}
 </style>
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 5f711e3e4f..4d6f32f9a9 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -29,7 +29,7 @@
 					<MkInput v-model="ad.ratio" type="number">
 						<template #label>{{ i18n.ts.ratio }}</template>
 					</MkInput>
-					<MkInput v-model="ad.expiresAt" type="date">
+					<MkInput v-model="ad.expiresAt" type="datetime-local">
 						<template #label>{{ i18n.ts.expiration }}</template>
 					</MkInput>
 				</FormSplit>
@@ -61,7 +61,12 @@ import { definePageMetadata } from '@/scripts/page-metadata';
 let ads: any[] = $ref([]);
 
 os.api('admin/ad/list').then(adsResponse => {
-	ads = adsResponse;
+	ads = adsResponse.map(r => {
+		return {
+			...r,
+			expiresAt: new Date(r.expiresAt).toISOString().slice(0, 16),
+		};
+	});
 });
 
 function add() {
diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue
index bb55881a22..b7727ca30d 100644
--- a/packages/frontend/src/pages/auth.vue
+++ b/packages/frontend/src/pages/auth.vue
@@ -77,6 +77,8 @@ export default defineComponent({
 		accepted() {
 			this.state = 'accepted';
 			if (this.session.app.callbackUrl) {
+				const url = new URL(this.session.app.callbackUrl);
+				if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url');
 				location.href = `${this.session.app.callbackUrl}?token=${this.session.token}`;
 			}
 		}, onLogin(res) {
diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue
index 3debaeeb61..9a4019e5b1 100644
--- a/packages/frontend/src/pages/miauth.vue
+++ b/packages/frontend/src/pages/miauth.vue
@@ -70,7 +70,7 @@ async function accept(): Promise<void> {
 	state = 'accepted';
 	if (props.callback) {
 		const cbUrl = new URL(props.callback);
-		if (!['http:', 'https:'].includes(cbUrl.protocol)) throw new Error('invalid url');
+		if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(cbUrl.protocol)) throw new Error('invalid url');
 		cbUrl.searchParams.set('session', props.session);
 		location.href = cbUrl.href;
 	}
diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index 7e8996f5df..5a465d7873 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -127,12 +127,11 @@ hr {
 }
 
 .ti {
-	vertical-align: -14%;
+	vertical-align: -12%;
 	line-height: 1em;
 
 	&:before {
-		display: inline-block;
-		font-size: 130%;
+		font-size: 128%;
 	}
 }
 
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index eac7e7e856..65a1ce0fce 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -142,10 +142,10 @@ mainRouter.on('change', () => {
 document.documentElement.style.overflowY = 'scroll';
 
 if (window.innerWidth > 1024) {
-	const tempUI = miLocalStorage.getItem('ui_temp')
+	const tempUI = miLocalStorage.getItem('ui_temp');
 	if (tempUI) {
-		miLocalStorage.setItem('ui', tempUI)
-		miLocalStorage.removeItem('ui_temp')
+		miLocalStorage.setItem('ui', tempUI);
+		miLocalStorage.removeItem('ui_temp');
 		location.reload();
 	}
 }