From my experience as a MYBB Big Board Owner, I’ll tell you that the bigger your forum gets, the more you need to maximise your crawl budget. While plugins like Google SEO (Updated for PHP 8) prevent duplicate content issues, you still have to worry about your crawl budget.
Your headaches are the:
- Post pages
- Archive pages
- Print Thread pages
You could have only a few threads or posts yet so many URLs pointing to these few content. MYBB’s URL structure needs to be revisited in my opinion.
LLMs and Search engines love your archive and print thread pages because they’re lightweight and text rich. The problem begins when they start indexing/referencing these pages instead of the full versions (where your structure, ads or tracking scripts reside). If you’re cool with training LLMs without maximizing visits then you could leave these pages, else, now is a good time to properly redirect these pages.
Managing the Hovatek Forum
The Hovatek Forum has been running since 2012, and on MYBB. I’d started off as forum.hovatek.com but later switched to hovatek.com/forum (I’ll talk about my reasons in a future post). Here are some things I put in place to reduce the multiple URLs generated by MYBB.
Modifying postbit_url template
I modified the href attribute at Post Bit Templates > postbit_posturl template from:
href="{$post['postlink']}#pid{$post['pid']}"
to
href="#pid{$post['pid']}"
Adding Htacess rules
I added the following htaccess rules to redirect the print thread and archive pages to their corresponding full versions
# Redirect printthread
<IfModule mod_rewrite.c>
RewriteEngine On
# /printthread.php?tid=N&page=P to /forum/thread-N-page-P.html
RewriteCond %{QUERY_STRING} ^tid=([0-9]+)&page=([0-9]+)$
RewriteRule ^printthread\.php$ /forum/thread-%1-page-%2.html? [R=301,L]
# /printthread.php?tid=N to /forum/thread-N.html
RewriteCond %{QUERY_STRING} ^tid=([0-9]+)$
RewriteRule ^printthread\.php$ /forum/thread-%1.html? [R=301,L]
# /archive/index.php -> /forum/index.php
RewriteCond %{QUERY_STRING} ^$
RewriteRule ^archive/index\.php$ /forum/index.php [R=301,L]
# /archive/index.php?forum-N.html -> /forum/forum-N.html
RewriteCond %{QUERY_STRING} ^forum-([0-9]+)\.html$
RewriteRule ^archive/index\.php$ /forum/forum-%1.html? [R=301,L]
# /archive/index.php?forum-N-P.html -> /forum/forum-N-page-P.html
RewriteCond %{QUERY_STRING} ^forum-([0-9]+)-([0-9]+)\.html$
RewriteRule ^archive/index\.php$ /forum/forum-%1-page-%2.html [R=301,L]
# /archive/index.php?thread-N.html -> /forum/thread-N.html
RewriteCond %{QUERY_STRING} ^thread-([0-9]+)\.html$
RewriteRule ^archive/index\.php$ /forum/thread-%1.html [R=301,L]
# /archive/index.php?thread-N-P.html -> /forum/thread-N-page-P.html
RewriteCond %{QUERY_STRING} ^thread-([0-9]+)-([0-9]+)\.html$
RewriteRule ^archive/index\.php$ /forum/thread-%1-page-%2.html [R=301,L]
# /archive/index.php/thread-N.html -> /forum/thread-N.html
RewriteRule ^archive/index\.php/thread-([0-9]+)\.html$ /forum/thread-$1.html [R=301,L]
# /archive/index.php/thread-N-P.html -> /forum/thread-N-page-P.html
RewriteRule ^archive/index\.php/thread-([0-9]+)-([0-9]+)\.html$ /forum/thread-$1-page-$2.html [R=301,L]
</IfModule>
Creating a redirect plugin
I created a plugin to redirect -post URLs to their corresponding -page URLs. The plugin gets the post ID (if specified) then enumerates the thread ID (if unspecified). It then queries the database for visible (to the user) posts in the given thread before the post ID and splits the count by the number of threads per page; thereby calculating the page where the post belongs.
<?php
$plugins->add_hook("showthread_start", "redirect_thread_run");
// Disallow direct access to this file for security reasons
if (!defined("IN_MYBB")) {
die("Direct initialization of this file is not allowed.");
}
function redirect_post_urls_info()
{
return array(
"name" => "Redirect Post URLs",
"description" => "Redirect -post urls to -page",
"website" => "https://www.hovatek.com",
"author" => "Team Hovatek",
"authorsite" => "https://www.hovatek.com",
"version" => "1.1",
"compatibility" => "*"
);
}
function myplugin_install() {}
function myplugin_is_installed() {}
function myplugin_uninstall() {}
function myplugin_activate() {}
function myplugin_deactivate() {}
function redirect_thread_run()
{
global $db, $mybb;
// If there is a pid
if (!empty($mybb->input['pid'])) {
// Set here to fetch only approved/deleted posts (and then below for a moderator we change this).
$visible_states = array("1");
$visible_condition = "visible IN (" . implode(',', array_unique($visible_states)) . ")";
// Allow viewing own unapproved threads for logged in users
if ($mybb->user['uid'] && $mybb->settings['showownunapproved']) {
$own_unapproved = ' AND (%1$s' . $visible_condition . ' OR (%1$svisible=0 AND %1$suid=' . (int)$mybb->user['uid'] . '))';
$visibleonly_p = sprintf($own_unapproved, 'p.');
} else {
$visibleonly_p = " AND p." . $visible_condition;
}
// If there is no tid but a pid, trick the system into thinking there was a tid anyway.
if (!isset($mybb->input['tid'])) {
// see if we already have the post information
if (isset($style) && $style['pid'] == $mybb->get_input('pid', MyBB::INPUT_INT) && $style['tid']) {
$mybb->input['tid'] = $style['tid'];
} else {
$options = array(
"limit" => 1
);
$query = $db->simple_select("posts", "fid,tid,visible", "pid=" . $mybb->get_input('pid', MyBB::INPUT_INT), $options);
$post = $db->fetch_array($query);
if (
empty($post) ||
(
$post['visible'] == 0 && !(
is_moderator($post['fid'], 'canviewunapprove') ||
($mybb->user['uid'] && $post['uid'] == $mybb->user['uid'] && $mybb->settings['showownunapproved'])
)
) ||
($post['visible'] == -1 && !is_moderator($post['fid'], 'canviewdeleted'))
) {
// post does not exist --> show corresponding error
error($lang->error_invalidpost);
}
$mybb->input['tid'] = $post['tid'];
}
}
// Assuming $pid and $tid (thread id) are already known/set
$pid = (int)$mybb->input['pid'];
$tid = (int)$mybb->input['tid'];
// Get posts per page setting
$perpage = $mybb->settings['postsperpage'];
if (!$perpage) {
$perpage = 10; // fallback default
}
// Count how many posts come before this specific post in the same thread
$query = $db->query("
SELECT COUNT(p.dateline) AS count
FROM " . TABLE_PREFIX . "posts p
WHERE p.tid = '" . $tid . "'
AND p.dateline <= (SELECT dateline FROM " . TABLE_PREFIX . "posts WHERE pid = '$pid')
{$visibleonly_p}
");
$result = $db->fetch_field($query, "count");
$page = ceil($result / $perpage);
$thread_url = $page > 1 ? "thread-" . $tid . "-page-" . $page : "thread-" . $tid;
// Redirect -post to -page with 301
header("Location: {$mybb->settings['bburl']}/{$thread_url}.html#pid{$pid}", true, 301);
}
}