diff --git a/CHANGELOG.md b/CHANGELOG.md
index eddcd86b0b..9380f23cfb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -54,6 +54,7 @@
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/869)
 - Fix: `/api/pages/update`にて`name`を指定せずにリクエストするとエラーが発生する問題を修正
 - Fix: AIセンシティブ判定が arm64 環境で動作しない問題を修正
+- Fix: 非Misskey系のソフトウェアからHTML`<ruby>`タグを含むノートを受信した場合、MFMの読み仮名(ルビ)文法に変換して表示
 
 ### Misskey.js
 - Feat: allow setting `binaryType` of WebSocket connection
diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts
index 8061622340..bf06d4457e 100644
--- a/packages/backend/src/core/MfmService.ts
+++ b/packages/backend/src/core/MfmService.ts
@@ -171,6 +171,39 @@ export class MfmService {
 					break;
 				}
 
+				case 'ruby': {
+					let ruby: [string, string][] = [];
+					for (const child of node.childNodes) {
+						if (child.nodeName === 'rp') {
+							continue;
+						}
+						if (treeAdapter.isTextNode(child) && !/\s|\[|\]/.test(child.value)) {
+							ruby.push([child.value, '']);
+							continue;
+						}
+						if (child.nodeName === 'rt' && ruby.length > 0) {
+							const rt = getText(child);
+							if (/\s|\[|\]/.test(rt)) {
+								// If any space is included in rt, it is treated as a normal text
+								ruby = [];
+								appendChildren(node.childNodes);
+								break;
+							} else {
+								ruby.at(-1)![1] = rt;
+								continue;
+							}
+						}
+						// If any other element is included in ruby, it is treated as a normal text
+						ruby = [];
+						appendChildren(node.childNodes);
+						break;
+					}
+					for (const [base, rt] of ruby) {
+						text += `$[ruby ${base} ${rt}]`;
+					}
+					break;
+				}
+
 				// block code (<pre><code>)
 				case 'pre': {
 					if (node.childNodes.length === 1 && node.childNodes[0].nodeName === 'code') {
diff --git a/packages/backend/test/unit/MfmService.ts b/packages/backend/test/unit/MfmService.ts
index fd4a03413b..36af8823f6 100644
--- a/packages/backend/test/unit/MfmService.ts
+++ b/packages/backend/test/unit/MfmService.ts
@@ -108,6 +108,24 @@ describe('MfmService', () => {
 			assert.deepStrictEqual(mfmService.fromHtml('<p>a <a></a> d</p>'), 'a  d');
 		});
 
+		test('ruby', () => {
+			assert.deepStrictEqual(mfmService.fromHtml('<p>a <ruby>Misskey<rp>(</rp><rt>ミスキー</rt><rp>)</rp></ruby> b</p>'), 'a $[ruby Misskey ミスキー] b');
+			assert.deepStrictEqual(mfmService.fromHtml('<p>a <ruby>Misskey<rp>(</rp><rt>ミスキー</rt><rp>)</rp>Misskey<rp>(</rp><rt>ミスキー</rt><rp>)</rp></ruby> b</p>'), 'a $[ruby Misskey ミスキー]$[ruby Misskey ミスキー] b');
+		});
+
+		test('ruby with spaces', () => {
+			assert.deepStrictEqual(mfmService.fromHtml('<p>a <ruby>Miss key<rp>(</rp><rt>ミスキー</rt><rp>)</rp> b</ruby> c</p>'), 'a Miss key(ミスキー) b c');
+			assert.deepStrictEqual(mfmService.fromHtml('<p>a <ruby>Misskey<rp>(</rp><rt>ミス キー</rt><rp>)</rp> b</ruby> c</p>'), 'a Misskey(ミス キー) b c');
+			assert.deepStrictEqual(
+				mfmService.fromHtml('<p>a <ruby>Misskey<rp>(</rp><rt>ミスキー</rt><rp>)</rp>Misskey<rp>(</rp><rt>ミス キー</rt><rp>)</rp>Misskey<rp>(</rp><rt>ミスキー</rt><rp>)</rp></ruby> b</p>'),
+				'a Misskey(ミスキー)Misskey(ミス キー)Misskey(ミスキー) b'
+			);
+		});
+
+		test('ruby with other inline tags', () => {
+			assert.deepStrictEqual(mfmService.fromHtml('<p>a <ruby><strong>Misskey</strong><rp>(</rp><rt>ミスキー</rt><rp>)</rp> b</ruby> c</p>'), 'a **Misskey**(ミスキー) b c');
+		});
+
 		test('mention', () => {
 			assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/@user" class="u-url mention">@user</a> d</p>'), 'a @user@example.com d');
 		});