diff --git a/packages/client/src/nirax.ts b/packages/client/src/nirax.ts
index 6db6335669a6ed8fd293dd34b107a645cd82ee0f..9dc787de4e5f7733dd39f01021360d79bd194938 100644
--- a/packages/client/src/nirax.ts
+++ b/packages/client/src/nirax.ts
@@ -8,6 +8,7 @@ type RouteDef = {
 	component: Component;
 	query?: Record<string, string>;
 	name?: string;
+	hash?: string;
 	globalCacheKey?: string;
 };
 
@@ -78,7 +79,12 @@ export class Router extends EventEmitter<{
 
 	public resolve(path: string): { route: RouteDef; props: Map<string, string>; } | null {
 		let queryString: string | null = null;
+		let hash: string | null = null;
 		if (path[0] === '/') path = path.substring(1);
+		if (path.includes('#')) {
+			hash = path.substring(path.indexOf('#') + 1);
+			path = path.substring(0, path.indexOf('#'));
+		}
 		if (path.includes('?')) {
 			queryString = path.substring(path.indexOf('?') + 1);
 			path = path.substring(0, path.indexOf('?'));
@@ -127,6 +133,10 @@ export class Router extends EventEmitter<{
 
 			if (parts.length !== 0) continue forEachRouteLoop;
 
+			if (route.hash != null && hash != null) {
+				props.set(route.hash, hash);
+			}
+
 			if (route.query != null && queryString != null) {
 				const queryObject = [...new URLSearchParams(queryString).entries()]
 					.reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {});
@@ -138,6 +148,7 @@ export class Router extends EventEmitter<{
 					}
 				}
 			}
+
 			return {
 				route,
 				props,
diff --git a/packages/client/src/pages/about.vue b/packages/client/src/pages/about.vue
index de89e3593cbdb3ca66ac5d1742718be1198256ea..683f7446b89888e2d79f1886d5981847d37eb09f 100644
--- a/packages/client/src/pages/about.vue
+++ b/packages/client/src/pages/about.vue
@@ -95,8 +95,14 @@ import number from '@/filters/number';
 import { i18n } from '@/i18n';
 import { definePageMetadata } from '@/scripts/page-metadata';
 
+const props = withDefaults(defineProps<{
+	initialTab?: string;
+}>(), {
+	initialTab: 'overview',
+});
+
 let stats = $ref(null);
-let tab = $ref('overview');
+let tab = $ref(props.initialTab);
 
 const initStats = () => os.api('stats', {
 }).then((res) => {
diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue
index 5ffbe349550bce44010a7062a13a2c13457b1baa..d967a081b72f462975ddad2077ea3f3999b24610 100644
--- a/packages/client/src/pages/admin/index.vue
+++ b/packages/client/src/pages/admin/index.vue
@@ -109,7 +109,7 @@ const menuDef = $computed(() => [{
 	}, {
 		icon: 'fas fa-globe',
 		text: i18n.ts.federation,
-		to: '/admin/federation',
+		to: '/about#federation',
 		active: props.initialPage === 'federation',
 	}, {
 		icon: 'fas fa-clipboard-list',
diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts
index a452a8bb3d2283eab47a0ed9820e101f0989c7c7..d257612f359cddf3232449e6e33e6f7aee9cfe74 100644
--- a/packages/client/src/router.ts
+++ b/packages/client/src/router.ts
@@ -50,6 +50,7 @@ export const routes = [{
 }, {
 	path: '/about',
 	component: page(() => import('./pages/about.vue')),
+	hash: 'initialTab',
 }, {
 	path: '/about-misskey',
 	component: page(() => import('./pages/about-misskey.vue')),
@@ -203,7 +204,7 @@ export const routes = [{
 	component: page(() => import('./pages/not-found.vue')),
 }];
 
-export const mainRouter = new Router(routes, location.pathname + location.search);
+export const mainRouter = new Router(routes, location.pathname + location.search + location.hash);
 
 window.history.replaceState({ key: mainRouter.getCurrentKey() }, '', location.href);
 
@@ -228,7 +229,7 @@ mainRouter.addListener('push', ctx => {
 });
 
 window.addEventListener('popstate', (event) => {
-	mainRouter.change(location.pathname + location.search, event.state?.key);
+	mainRouter.change(location.pathname + location.search + location.hash, event.state?.key);
 	const scrollPos = scrollPosStore.get(event.state?.key) ?? 0;
 	window.scroll({ top: scrollPos, behavior: 'instant' });
 	window.setTimeout(() => { // 遷移直後はタイミングによってはコンポーネントが復元し切ってない可能性も考えられるため少し時間を空けて再度スクロール