diff --git a/database/2025_02_08_213207_moved_forum_signature_to_dedicated_table.php b/database/2025_02_08_213207_moved_forum_signature_to_dedicated_table.php
new file mode 100644
index 00000000..f0a08591
--- /dev/null
+++ b/database/2025_02_08_213207_moved_forum_signature_to_dedicated_table.php
@@ -0,0 +1,36 @@
+<?php
+use Index\Db\DbConnection;
+use Index\Db\Migration\DbMigration;
+
+final class MovedForumSignatureToDedicatedTable_20250208_213207 implements DbMigration {
+    public function migrate(DbConnection $conn): void {
+        $conn->execute(<<<SQL
+            CREATE TABLE msz_forum_signatures (
+                user_id               INT UNSIGNED       NOT NULL,
+                signature_body        TEXT               NOT NULL COLLATE 'utf8mb4_unicode_520_ci',
+                signature_body_format ENUM('','bb','md') NOT NULL COLLATE 'ascii_general_ci',
+                signature_created     TIMESTAMP          NOT NULL DEFAULT NOW(),
+                PRIMARY KEY (user_id),
+                CONSTRAINT forum_signatures_users_foreign
+                    FOREIGN KEY (user_id)
+                    REFERENCES msz_users (user_id)
+                    ON UPDATE CASCADE
+                    ON DELETE CASCADE
+            ) COLLATE='utf8mb4_bin';
+        SQL);
+
+        $conn->execute(<<<SQL
+            INSERT INTO msz_forum_signatures
+            SELECT user_id, user_signature_content, user_signature_content_format, NOW()
+            FROM msz_users
+            WHERE user_signature_content IS NOT NULL
+            AND TRIM(user_signature_content) <> ''
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_users
+                DROP COLUMN user_signature_content,
+                DROP COLUMN user_signature_content_format;
+        SQL);
+    }
+}
diff --git a/public-legacy/profile.php b/public-legacy/profile.php
index d81427be..84975433 100644
--- a/public-legacy/profile.php
+++ b/public-legacy/profile.php
@@ -5,6 +5,7 @@ use stdClass;
 use InvalidArgumentException;
 use RuntimeException;
 use Index\ByteFormat;
+use Misuzu\Forum\ForumSignaturesData;
 use Misuzu\Parsers\TextFormat;
 use Misuzu\Profile\ProfileBackgroundAttach;
 use Misuzu\Users\{User,UsersContext};
@@ -79,6 +80,8 @@ $avatarAsset = new UserAvatarAsset($userInfo);
 $backgroundInfo = $msz->profileCtx->backgrounds->getProfileBackground($userInfo);
 $backgroundAsset = new UserBackgroundAsset($userInfo, $backgroundInfo);
 
+$sigInfo = $msz->forumCtx->signatures->getSignature($userInfo);
+
 if($isEditing) {
     if(!$canEdit)
         Template::throwError(403);
@@ -152,18 +155,22 @@ if($isEditing) {
                 }
             }
 
-            if(!empty($_POST['signature']) && is_array($_POST['signature'])) {
+            if(filter_has_var(INPUT_POST, 'sig_body')) {
                 if(!$perms->edit_signature) {
                     $notices[] = 'You\'re not allowed to edit your forum signature.';
                 } else {
-                    $sigText  = (string)($_POST['signature']['text'] ?? '');
-                    $sigParse = TextFormat::tryFrom((string)($_POST['signature']['parser'] ?? '')) ?? TextFormat::Plain;
-                    $sigValid = $msz->usersCtx->users->validateForumSignature($sigParse, $sigText);
-
-                    if($sigValid === '')
-                        $msz->usersCtx->users->updateUser($userInfo, signatureBody: $sigText, signatureBodyFormat: $sigParse);
-                    else
-                        $notices[] = $msz->usersCtx->users->validateForumSignatureText($sigValid);
+                    $sigBody = (string)filter_input(INPUT_POST, 'sig_body');
+                    if(trim($sigBody) === '') {
+                        $msz->forumCtx->signatures->deleteSignature($userInfo);
+                        $sigInfo = null;
+                    } else {
+                        $sigFormat = TextFormat::tryFrom(filter_input(INPUT_POST, 'sig_format'));
+                        $sigValid = ForumSignaturesData::validateSignature($sigFormat, $sigBody);
+                        if($sigValid === '')
+                            $sigInfo = $msz->forumCtx->signatures->updateSignature($userInfo, $sigBody, $sigFormat);
+                        else
+                            $notices[] = ForumSignaturesData::validateSignatureText($sigValid);
+                    }
                 }
             }
 
@@ -395,4 +402,5 @@ Template::render('profile.index', [
     'profile_background_asset' => $backgroundAsset,
     'profile_can_send_messages' => $viewerPermsGlobal->check(Perm::G_MESSAGES_SEND),
     'profile_age' => $msz->usersCtx->birthdates->getUserAge($userInfo),
+    'profile_forum_signature_info' => $sigInfo,
 ]);
diff --git a/public-legacy/settings/data.php b/public-legacy/settings/data.php
index fd919016..a991f5c8 100644
--- a/public-legacy/settings/data.php
+++ b/public-legacy/settings/data.php
@@ -134,6 +134,7 @@ if(isset($_POST['action']) && is_string($_POST['action'])) {
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_posts',         ['comment_id:s', 'category_id:s', 'user_id:s:n', 'comment_reply_to:s:n', 'comment_text:s', 'comment_created:t', 'comment_pinned:t:n', 'comment_edited:t:n', 'comment_deleted:t:n']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_votes',         ['comment_id:s', 'user_id:s', 'comment_vote:i']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_posts',            ['post_id:s', 'topic_id:s', 'forum_id:s', 'user_id:s:n', 'post_remote_addr:a', 'post_text:s', 'post_text_format:s', 'post_display_signature:b', 'post_created:t', 'post_edited:t:n', 'post_deleted:t:n']);
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_signatures',       ['user_id:s', 'signature_body:s', 'signature_body_format:s', 'signature_created:t']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics',           ['topic_id:s', 'forum_id:s', 'user_id:s:n', 'topic_type:i', 'topic_title:s', 'topic_count_views:i', 'topic_created:t', 'topic_bumped:t', 'topic_deleted:t:n', 'topic_locked:t:n']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics_redirects', ['topic_id:s', 'user_id:s:n', 'redir_url:s', 'redir_created:t']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics_track',     ['user_id:s', 'topic_id:s', 'forum_id:s', 'track_last_read:t']);
@@ -149,7 +150,7 @@ if(isset($_POST['action']) && is_string($_POST['action'])) {
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'profile_backgrounds',    ['user_id:s', 'bg_attach:s', 'bg_blend:i', 'bg_slide:i']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'profile_fields_values',  ['field_id:s', 'user_id:s', 'format_id:s', 'field_value:s']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'sessions',               ['session_id:s', 'user_id:s', 'session_key:n', 'session_remote_addr_first:a', 'session_remote_addr_last:a:n', 'session_user_agent:s', 'session_country:s', 'session_expires:t', 'session_expires_bump:b', 'session_created:t', 'session_active:t:n']);
-                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users',                  ['user_id:s', 'user_name:s', 'user_password:n', 'user_email:s', 'user_remote_addr_first:a', 'user_remote_addr_last:a', 'user_super:b', 'user_country:s', 'user_colour:i:n', 'user_created:t', 'user_active:t:n', 'user_deleted:t:n', 'user_display_role_id:s:n', 'user_about_content:s:n', 'user_about_content_format:s', 'user_signature_content:s:n', 'user_signature_content_format:s', 'user_title:s:n']);
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users',                  ['user_id:s', 'user_name:s', 'user_password:n', 'user_email:s', 'user_remote_addr_first:a', 'user_remote_addr_last:a', 'user_super:b', 'user_country:s', 'user_colour:i:n', 'user_created:t', 'user_active:t:n', 'user_deleted:t:n', 'user_display_role_id:s:n', 'user_about_content:s:n', 'user_about_content_format:s', 'user_title:s:n']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_bans',             ['ban_id:s', 'user_id:s', 'mod_id:n', 'ban_severity:i', 'ban_reason_public:s', 'ban_reason_private:s', 'ban_created:t', 'ban_expires:t:n']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_birthdates',       ['user_id:s', 'birth_year:i:n', 'birth_month:i', 'birth_day:i']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_password_resets',  ['reset_id:s', 'user_id:s', 'reset_remote_addr:a', 'reset_requested:t', 'reset_code:n']);
diff --git a/src/Forum/ForumContext.php b/src/Forum/ForumContext.php
index 1cb142ca..3b90ba87 100644
--- a/src/Forum/ForumContext.php
+++ b/src/Forum/ForumContext.php
@@ -3,6 +3,7 @@ namespace Misuzu\Forum;
 
 use stdClass;
 use Index\Db\DbConnection;
+use Misuzu\Parsers\TextFormat;
 use Misuzu\Users\UserInfo;
 
 class ForumContext {
@@ -10,6 +11,7 @@ class ForumContext {
     public private(set) ForumTopicsData $topics;
     public private(set) ForumTopicRedirectsData $topicRedirects;
     public private(set) ForumPostsData $posts;
+    public private(set) ForumSignaturesData $signatures;
 
     /** @var array<string, int> */
     private array $totalUserTopics = [];
@@ -17,11 +19,15 @@ class ForumContext {
     /** @var array<string, int> */
     private array $totalUserPosts = [];
 
+    /** @var array<string, ForumSignatureInfo> */
+    private array $cachedSignatures = [];
+
     public function __construct(DbConnection $dbConn) {
         $this->categories = new ForumCategoriesData($dbConn);
         $this->topics = new ForumTopicsData($dbConn);
         $this->topicRedirects = new ForumTopicRedirectsData($dbConn);
         $this->posts = new ForumPostsData($dbConn);
+        $this->signatures = new ForumSignaturesData($dbConn);
     }
 
     // should be replaced by a static counter
@@ -47,4 +53,15 @@ class ForumContext {
 
         return $this->totalUserPosts[$userId] = $this->posts->countPosts(userInfo: $userInfo, deleted: false);
     }
+
+    public function getCachedSignature(UserInfo|string|null $userInfo): ?ForumSignatureInfo {
+        if($userInfo === null)
+            return null;
+
+        $userId = $userInfo instanceof UserInfo ? $userInfo->id : $userInfo;
+        if(array_key_exists($userId, $this->cachedSignatures))
+            return $this->cachedSignatures[$userId];
+
+        return $this->cachedSignatures[$userId] = $this->signatures->getSignature($userInfo);
+    }
 }
diff --git a/src/Forum/ForumSignatureInfo.php b/src/Forum/ForumSignatureInfo.php
new file mode 100644
index 00000000..0af68227
--- /dev/null
+++ b/src/Forum/ForumSignatureInfo.php
@@ -0,0 +1,40 @@
+<?php
+namespace Misuzu\Forum;
+
+use Carbon\CarbonImmutable;
+use Index\Db\DbResult;
+use Misuzu\Parsers\TextFormat;
+
+class ForumSignatureInfo {
+    public function __construct(
+        public private(set) string $userId,
+        public private(set) string $body,
+        public private(set) TextFormat $bodyFormat,
+        public private(set) int $createdTime,
+    ) {}
+
+    public static function fromResult(DbResult $result): self {
+        return new ForumSignatureInfo(
+            userId: $result->getString(0),
+            body: $result->getString(1),
+            bodyFormat: TextFormat::tryFrom($result->getString(2)) ?? TextFormat::Plain,
+            createdTime: $result->getInteger(3),
+        );
+    }
+
+    public CarbonImmutable $createdAt {
+        get => CarbonImmutable::createFromTimestampUTC($this->createdTime);
+    }
+
+    public bool $isBodyPlain {
+        get => $this->bodyFormat === TextFormat::Plain;
+    }
+
+    public bool $isBodyBBCode {
+        get => $this->bodyFormat === TextFormat::BBCode;
+    }
+
+    public bool $isBodyMarkdown {
+        get => $this->bodyFormat === TextFormat::Markdown;
+    }
+}
diff --git a/src/Forum/ForumSignaturesData.php b/src/Forum/ForumSignaturesData.php
new file mode 100644
index 00000000..f782f713
--- /dev/null
+++ b/src/Forum/ForumSignaturesData.php
@@ -0,0 +1,84 @@
+<?php
+namespace Misuzu\Forum;
+
+use RuntimeException;
+use Index\Db\{DbConnection,DbStatementCache};
+use Misuzu\Parsers\TextFormat;
+use Misuzu\Users\UserInfo;
+
+class ForumSignaturesData {
+    public const int BODY_MAX_LENGTH = 2000;
+
+    private DbStatementCache $cache;
+
+    public function __construct(DbConnection $dbConn) {
+        $this->cache = new DbStatementCache($dbConn);
+    }
+
+    public function getSignature(UserInfo|string $userInfo): ?ForumSignatureInfo {
+        $stmt = $this->cache->get(<<<SQL
+            SELECT user_id, signature_body, signature_body_format, UNIX_TIMESTAMP(signature_created)
+            FROM msz_forum_signatures
+            WHERE user_id = ?
+        SQL);
+        $stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
+        $stmt->execute();
+
+        $result = $stmt->getResult();
+        return $result->next() ? ForumSignatureInfo::fromResult($result) : null;
+    }
+
+    public function deleteSignature(UserInfo|string $userInfo): void {
+        $stmt = $this->cache->get(<<<SQL
+            DELETE FROM msz_forum_signatures
+            WHERE user_id = ?
+        SQL);
+        $stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
+        $stmt->execute();
+    }
+
+    public function updateSignature(
+        UserInfo|string $userInfo,
+        string $body,
+        TextFormat|string $format
+    ): ForumSignatureInfo {
+        if(is_string($format))
+            $format = TextFormat::from($format);
+
+        $stmt = $this->cache->get(<<<SQL
+            REPLACE INTO msz_forum_signatures (
+                user_id, signature_body, signature_body_format
+            ) VALUES (?, ?, ?)
+        SQL);
+        $stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
+        $stmt->nextParameter($body);
+        $stmt->nextParameter($format->value);
+        $stmt->execute();
+
+        $sigInfo = $this->getSignature($userInfo);
+        if($sigInfo === null)
+            throw new RuntimeException('failed to update forum signature');
+
+        return $sigInfo;
+    }
+
+    public static function validateSignature(?TextFormat $format, string $text): string {
+        if($format === null)
+            return 'parser';
+
+        $length = mb_strlen($text);
+        if($length > self::BODY_MAX_LENGTH)
+            return 'long';
+
+        return '';
+    }
+
+    public static function validateSignatureText(string $error): string {
+        return match($error) {
+            'parser' => 'You attempted to select an invalid parser for your forum signature.',
+            'long' => sprintf('Please keep the length of your forum signature below %d characters.', self::BODY_MAX_LENGTH),
+            '' => 'Your forum signature is fine, why are you seeing this?',
+            default => 'Your forum signature is not acceptable.',
+        };
+    }
+}
diff --git a/src/Forum/ForumTopicsRoutes.php b/src/Forum/ForumTopicsRoutes.php
index d24b6e18..cf616d78 100644
--- a/src/Forum/ForumTopicsRoutes.php
+++ b/src/Forum/ForumTopicsRoutes.php
@@ -15,7 +15,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
     use RouteHandlerCommon, UrlSourceCommon;
 
     public function __construct(
-        private ForumContext $forum,
+        private ForumContext $forumCtx,
         private UsersContext $usersCtx,
         private AuditLogData $auditLog,
         private AuthInfo $authInfo,
@@ -26,7 +26,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
     public function getTopic(HttpResponseBuilder $response, HttpRequest $request, string $topicId): mixed {
         $isNuked = $deleted = $canDeleteAny = false;
         try {
-            $topic = $this->forum->topics->getTopic(topicId: $topicId);
+            $topic = $this->forumCtx->topics->getTopic(topicId: $topicId);
 
             $deleted = $topic->deleted;
             $perms = $this->authInfo->getPerms('forum', $topic->categoryId);
@@ -39,8 +39,8 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
         }
 
         if($isNuked || $deleted) {
-            if($this->forum->topicRedirects->hasTopicRedirect($topicId)) {
-                $redirect = $this->forum->topicRedirects->getTopicRedirect($topicId);
+            if($this->forumCtx->topicRedirects->hasTopicRedirect($topicId)) {
+                $redirect = $this->forumCtx->topicRedirects->getTopicRedirect($topicId);
                 Template::set('topic_redir_info', $redirect);
 
                 if($isNuked || !$canDeleteAny) {
@@ -70,7 +70,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
         // this should be in the config
         $deletePostThreshold = 1;
 
-        $categoryInfo = $this->forum->categories->getCategory(topicInfo: $topic);
+        $categoryInfo = $this->forumCtx->categories->getCategory(topicInfo: $topic);
         $isFrozen = $categoryInfo->archived || $deleted;
         $canDeleteOwn = !$isFrozen && !$topic->locked && $perms->check(Perm::F_POST_DELETE_OWN);
         $canBump = !$isFrozen && $perms->check(Perm::F_TOPIC_BUMP);
@@ -86,7 +86,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             )
         );
 
-        $postInfos = $this->forum->posts->getPosts(
+        $postInfos = $this->forumCtx->posts->getPosts(
             topicInfo: $topic,
             deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false,
             pagination: $pagination,
@@ -95,7 +95,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             return 404;
 
         try {
-            $originalPostInfo = $this->forum->posts->getPost(topicInfo: $topic);
+            $originalPostInfo = $this->forumCtx->posts->getPost(topicInfo: $topic);
         } catch(RuntimeException $ex) {
             return 404;
         }
@@ -109,7 +109,13 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             if($postInfo->userId !== null) {
                 $post->user = $this->usersCtx->getUserInfo($postInfo->userId);
                 $post->colour = $this->usersCtx->getUserColour($post->user);
-                $post->postsCount = $this->forum->countTotalUserPosts($post->user);
+                $post->postsCount = $this->forumCtx->countTotalUserPosts($post->user);
+
+                if($postInfo->shouldDisplaySignature) {
+                    $sigInfo = $this->forumCtx->getCachedSignature($post->user);
+                    if($sigInfo !== null)
+                        $post->signature = $sigInfo;
+                }
             }
 
             $post->isOriginalPost = $originalPostInfo->id == $postInfo->id;
@@ -117,14 +123,14 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
                 && $originalPostInfo->userId === $postInfo->userId;
         }
 
-        if(!$this->forum->topics->checkUserHasReadTopic($this->authInfo->userInfo, $topic))
-            $this->forum->topics->incrementTopicViews($topic);
+        if(!$this->forumCtx->topics->checkUserHasReadTopic($this->authInfo->userInfo, $topic))
+            $this->forumCtx->topics->incrementTopicViews($topic);
 
-        $this->forum->topics->updateUserReadTopic($this->authInfo->userInfo, $topic);
+        $this->forumCtx->topics->updateUserReadTopic($this->authInfo->userInfo, $topic);
 
         return Template::renderRaw('forum.topic', [
-            'topic_breadcrumbs' => iterator_to_array($this->forum->categories->getCategoryAncestry($topic)),
-            'global_accent_colour' => $this->forum->categories->getCategoryColour($topic),
+            'topic_breadcrumbs' => iterator_to_array($this->forumCtx->categories->getCategoryAncestry($topic)),
+            'global_accent_colour' => $this->forumCtx->categories->getCategoryColour($topic),
             'topic_info' => $topic,
             'category_info' => $categoryInfo,
             'topic_posts' => $posts,
@@ -167,7 +173,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
         }
 
         try {
-            $topic = $this->forum->topics->getTopic(
+            $topic = $this->forumCtx->topics->getTopic(
                 topicId: $topicId,
                 deleted: false,
             );
@@ -231,7 +237,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             $deletePostThreshold = 1;
 
             // deleted posts are intentionally included
-            $topicPostCount = $this->forum->posts->countPosts(topicInfo: $topic);
+            $topicPostCount = $this->forumCtx->posts->countPosts(topicInfo: $topic);
             if($topicPostCount > $deletePostThreshold) {
                 $response->statusCode = 403;
                 return [
@@ -243,7 +249,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             }
         }
 
-        $category = $this->forum->categories->getCategory(topicInfo: $topic);
+        $category = $this->forumCtx->categories->getCategory(topicInfo: $topic);
         if($category->archived) {
             $response->statusCode = 400;
             return [
@@ -254,7 +260,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             ];
         }
 
-        $this->forum->topics->deleteTopic($topic);
+        $this->forumCtx->topics->deleteTopic($topic);
         $this->auditLog->createLog(
             $this->authInfo->userInfo,
             'FORUM_TOPIC_DELETE',
@@ -287,7 +293,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
         }
 
         try {
-            $topic = $this->forum->topics->getTopic(
+            $topic = $this->forumCtx->topics->getTopic(
                 topicId: $topicId,
                 deleted: true,
             );
@@ -323,7 +329,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             ];
         }
 
-        $category = $this->forum->categories->getCategory(topicInfo: $topic);
+        $category = $this->forumCtx->categories->getCategory(topicInfo: $topic);
         if($category->archived) {
             $response->statusCode = 400;
             return [
@@ -334,7 +340,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             ];
         }
 
-        $this->forum->topics->restoreTopic($topic);
+        $this->forumCtx->topics->restoreTopic($topic);
         $this->auditLog->createLog(
             $this->authInfo->userInfo,
             'FORUM_TOPIC_RESTORE',
@@ -367,7 +373,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
         }
 
         try {
-            $topic = $this->forum->topics->getTopic(
+            $topic = $this->forumCtx->topics->getTopic(
                 topicId: $topicId,
                 deleted: true,
             );
@@ -403,7 +409,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             ];
         }
 
-        $category = $this->forum->categories->getCategory(topicInfo: $topic);
+        $category = $this->forumCtx->categories->getCategory(topicInfo: $topic);
         if($category->archived) {
             $response->statusCode = 400;
             return [
@@ -414,7 +420,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             ];
         }
 
-        $this->forum->topics->nukeTopic($topic);
+        $this->forumCtx->topics->nukeTopic($topic);
         $this->auditLog->createLog(
             $this->authInfo->userInfo,
             'FORUM_TOPIC_NUKE',
@@ -447,7 +453,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
         }
 
         try {
-            $topic = $this->forum->topics->getTopic(
+            $topic = $this->forumCtx->topics->getTopic(
                 topicId: $topicId,
                 deleted: false,
             );
@@ -483,7 +489,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             ];
         }
 
-        $category = $this->forum->categories->getCategory(topicInfo: $topic);
+        $category = $this->forumCtx->categories->getCategory(topicInfo: $topic);
         if($category->archived) {
             $response->statusCode = 400;
             return [
@@ -494,7 +500,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             ];
         }
 
-        $this->forum->topics->bumpTopic($topic);
+        $this->forumCtx->topics->bumpTopic($topic);
         $this->auditLog->createLog(
             $this->authInfo->userInfo,
             'FORUM_TOPIC_BUMP',
@@ -527,7 +533,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
         }
 
         try {
-            $topic = $this->forum->topics->getTopic(
+            $topic = $this->forumCtx->topics->getTopic(
                 topicId: $topicId,
                 deleted: false,
             );
@@ -573,7 +579,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             ];
         }
 
-        $category = $this->forum->categories->getCategory(topicInfo: $topic);
+        $category = $this->forumCtx->categories->getCategory(topicInfo: $topic);
         if($category->archived) {
             $response->statusCode = 400;
             return [
@@ -584,7 +590,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             ];
         }
 
-        $this->forum->topics->lockTopic($topic);
+        $this->forumCtx->topics->lockTopic($topic);
         $this->auditLog->createLog(
             $this->authInfo->userInfo,
             'FORUM_TOPIC_LOCK',
@@ -617,7 +623,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
         }
 
         try {
-            $topic = $this->forum->topics->getTopic(
+            $topic = $this->forumCtx->topics->getTopic(
                 topicId: $topicId,
                 deleted: false,
             );
@@ -663,7 +669,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             ];
         }
 
-        $category = $this->forum->categories->getCategory(topicInfo: $topic);
+        $category = $this->forumCtx->categories->getCategory(topicInfo: $topic);
         if($category->archived) {
             $response->statusCode = 400;
             return [
@@ -674,7 +680,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
             ];
         }
 
-        $this->forum->topics->unlockTopic($topic);
+        $this->forumCtx->topics->unlockTopic($topic);
         $this->auditLog->createLog(
             $this->authInfo->userInfo,
             'FORUM_TOPIC_UNLOCK',
diff --git a/src/Users/UserInfo.php b/src/Users/UserInfo.php
index 95ecf190..ea95ab57 100644
--- a/src/Users/UserInfo.php
+++ b/src/Users/UserInfo.php
@@ -23,8 +23,6 @@ class UserInfo {
         public private(set) ?string $displayRoleId,
         public private(set) ?string $aboutBody,
         public private(set) TextFormat $aboutBodyFormat,
-        public private(set) ?string $signatureBody,
-        public private(set) TextFormat $signatureBodyFormat,
         public private(set) ?string $title,
     ) {}
 
@@ -45,9 +43,7 @@ class UserInfo {
             displayRoleId: $result->getStringOrNull(12),
             aboutBody: $result->getStringOrNull(13),
             aboutBodyFormat: TextFormat::tryFrom($result->getString(14)) ?? TextFormat::Plain,
-            signatureBody: $result->getStringOrNull(15),
-            signatureBodyFormat: TextFormat::tryFrom($result->getString(16)) ?? TextFormat::Plain,
-            title: $result->getString(17),
+            title: $result->getString(15),
         );
     }
 
@@ -98,16 +94,4 @@ class UserInfo {
     public bool $isAboutBodyMarkdown {
         get => $this->aboutBodyFormat === TextFormat::Markdown;
     }
-
-    public bool $isSignatureBodyPlain {
-        get => $this->signatureBodyFormat === TextFormat::Plain;
-    }
-
-    public bool $isSignatureBodyBBCode {
-        get => $this->signatureBodyFormat === TextFormat::BBCode;
-    }
-
-    public bool $isSignatureBodyMarkdown {
-        get => $this->signatureBodyFormat === TextFormat::Markdown;
-    }
 }
diff --git a/src/Users/UsersData.php b/src/Users/UsersData.php
index 45f58e7e..649ff7a0 100644
--- a/src/Users/UsersData.php
+++ b/src/Users/UsersData.php
@@ -26,7 +26,6 @@ class UsersData {
     public const PASSWORD_UNIQUE = 6;
 
     public const PROFILE_ABOUT_MAX_LENGTH = 50000;
-    public const FORUM_SIGNATURE_MAX_LENGTH = 2000;
 
     public static function passwordHash(string $password): string {
         return password_hash($password, self::PASSWORD_ALGO, self::PASSWORD_OPTS);
@@ -178,7 +177,6 @@ class UsersData {
                 UNIX_TIMESTAMP(u.user_deleted),
                 u.user_display_role_id,
                 u.user_about_content, u.user_about_content_format,
-                u.user_signature_content, u.user_signature_content_format,
                 u.user_title
             FROM msz_users AS u
         SQL;
@@ -287,7 +285,6 @@ class UsersData {
                 UNIX_TIMESTAMP(user_deleted),
                 user_display_role_id,
                 user_about_content, user_about_content_format,
-                user_signature_content, user_signature_content_format,
                 user_title
             FROM msz_users
         SQL;
@@ -359,8 +356,6 @@ class UsersData {
         RoleInfo|string|null $displayRoleInfo = null,
         ?string $aboutBody = null,
         TextFormat|string|null $aboutBodyFormat = null,
-        ?string $signatureBody = null,
-        TextFormat|string|null $signatureBodyFormat = null,
         ?string $title = null
     ): void {
         if($userInfo instanceof UserInfo)
@@ -419,18 +414,6 @@ class UsersData {
             $values[] = $aboutBodyFormat->value;
         }
 
-        if($signatureBody !== null && $signatureBodyFormat !== null) {
-            if(is_string($signatureBodyFormat))
-                $signatureBodyFormat = TextFormat::tryFrom($signatureBodyFormat) ?? null;
-            if(self::validateForumSignature($signatureBodyFormat, $signatureBody) !== '')
-                throw new InvalidArgumentException('$signatureBody and $signatureBodyFormat contain invalid data!');
-
-            $fields[] = 'user_signature_content = ?';
-            $values[] = $signatureBody;
-            $fields[] = 'user_signature_content_format = ?';
-            $values[] = $signatureBodyFormat->value;
-        }
-
         if($title !== null) {
             $fields[] = 'user_title = ?';
             $values[] = $title;
@@ -726,24 +709,4 @@ class UsersData {
             default => 'Your profile about section is not acceptable.',
         };
     }
-
-    public static function validateForumSignature(?TextFormat $format, string $text): string {
-        if($format === null)
-            return 'parser';
-
-        $length = strlen($text);
-        if($length > self::FORUM_SIGNATURE_MAX_LENGTH)
-            return 'long';
-
-        return '';
-    }
-
-    public static function validateForumSignatureText(string $error): string {
-        return match($error) {
-            'parser' => 'You attempted to select an invalid parser for your forum signature.',
-            'long' => sprintf('Please keep the length of your forum signature below %d characters.', self::FORUM_SIGNATURE_MAX_LENGTH),
-            '' => 'Your forum signature is fine, why are you seeing this?',
-            default => 'Your forum signature is not acceptable.',
-        };
-    }
 }
diff --git a/templates/forum/macros.twig b/templates/forum/macros.twig
index 49acd475..bf1c3ee7 100644
--- a/templates/forum/macros.twig
+++ b/templates/forum/macros.twig
@@ -482,8 +482,8 @@
         {% set author_created = post.user.createdTime %}
         {% set author_posts_count = post.postsCount %}
         {% set author_is_op = post.isOriginalPoster %}
-        {% set signature_body = post.user.signatureBody|default('')|escape|parse_text(post.user.signatureBodyFormat) %}
-        {% set signature_is_markdown = post.user.isSignatureBodyMarkdown %}
+        {% set signature_body = post.signature.body|default('')|escape|parse_text(post.signature.bodyFormat|default('')) %}
+        {% set signature_is_markdown = post.signature.isBodyMarkdown|default(false) %}
     {% endif %}
 
     {% set viewer_is_author = has_author and user_id == author_id %}
diff --git a/templates/profile/index.twig b/templates/profile/index.twig
index 18cd786d..a5b0dc9b 100644
--- a/templates/profile/index.twig
+++ b/templates/profile/index.twig
@@ -276,18 +276,18 @@
                             </div>
                         {% endif %}
 
-                        {% if (not profile_is_banned or profile_can_edit) and ((profile_is_editing and perms.edit_signature) or profile_user.signatureBody is not empty) %}
+                        {% if (not profile_is_banned or profile_can_edit) and ((profile_is_editing and perms.edit_signature) or profile_forum_signature_info is not null) %}
                             <div class="container profile__container profile__signature" id="signature">
                                 {{ container_title('Signature') }}
 
                                 {% if profile_is_editing %}
                                     <div class="profile__signature__editor">
-                                        {{ input_select('signature[parser]', parser_options(), profile_user.signatureBodyFormat.value, '', '', false, 'profile__signature__select') }}
-                                        <textarea name="signature[text]" class="input__textarea profile__signature__text" id="signature-textarea">{{ profile_user.signatureBody }}</textarea>
+                                        {{ input_select('sig_format', parser_options(), profile_forum_signature_info.bodyFormat.value|default('bb'), '', '', false, 'profile__signature__select') }}
+                                        <textarea name="sig_body" class="input__textarea profile__signature__text" id="signature-textarea">{{ profile_forum_signature_info.body|default('') }}</textarea>
                                     </div>
                                 {% else %}
-                                    <div class="profile__signature__content{% if profile_is_editing %} profile__signature__content--edit{% elseif profile_user.isSignatureBodyMarkdown %} markdown{% endif %}">
-                                        {{ profile_user.signatureBody|escape|parse_text(profile_user.signatureBodyFormat)|raw }}
+                                    <div class="profile__signature__content{% if profile_is_editing %} profile__signature__content--edit{% elseif profile_forum_signature_info.isBodyMarkdown %} markdown{% endif %}">
+                                        {{ profile_forum_signature_info.body|escape|parse_text(profile_forum_signature_info.bodyFormat)|raw }}
                                     </div>
                                 {% endif %}
                             </div>