Most “XenForo is broken” tickets are a switch. Debug left on. Registered not primary. Guest page cache without a page context. Cloudflare minifying the JS 2.3 already deferred. An index route that ate the forum-list widgets. This is a catalogue of those switches — official manual and config.php first, community operations second — so you stop treating them as mysteries.
It is not the permissions primer and it is not the performance deep dive. Those articles own the theory. This one is the autopsy table.
1. Primary group is not Registered
Official groups chapter, the sentence XenForo repeats until you hear it: every registered human — including admins and moderators — keeps Registered as primary. Administrative and Moderating are secondary. New registrations always land in Registered.
What goes wrong when you “promote” someone by changing primary to VIP or Whitelisted:
- Every add-on that assumes Registered as the baseline now has two baselines
- Permission analysis becomes a novel
- A later promotion that removes
VIPcan leave them with a primary you did not mean - The next upgrade checklist asks you to re-tick twenty Yeses
Fix: primary back to Registered. Put the special role on a secondary group. Analyze permissions as that user. Off-peak — community math still says groups × nodes can lock a large tree.
2. Debug and full JS on production
Official config.php:
$config['debug'] = false;
$config['development']['fullJs'] = false;
Official cache/options docs: debug on a live site slows page generation and prints SQL to visitors. fullJs serves unminified modules and extra requests. Both are how a “quick look” at 17:00 becomes an incident.
Related official “never on live”: ipv4CidrMatch / ipv6CidrMatch = 0 (VPN session bypass for development).
3. Gzip off, or a CDN that “helps”
Official: $config['enableGzip'] = true compresses HTML/CSS XenForo emits. Turn it off only if the server or CDN already compresses and you measured.
Community Cloudflare: cache css.php. Disable Auto Minify if anything else already minifies (XFOptimize is the named example). Double-minify breaks 2.3’s deferred JS. Rocket Loader is not in the notebooks; treat any “rewrite scripts” toggle as the same class of mistake.
4. Cache enabled, page cache half-enabled
Official cache manual (docs.xenforo.com/manual/config/cache):
$config['cache']['enabled'] = trueplus a provider (Redis,Memcached,Filesystem,ApcCache, …)- Guest pages:
$config['pageCache']['enabled'] = trueand a dedicated$config['cache']['context']['page']provider. Without the context, page cache does not run. Official advice: separate instance so guest HTML does not evict sessions $config['cache']['sessions'] = trueneeds room. Official: do not session-cache into APC if it can fill
Symptom of the half-config: you “turned on page cache,” guests are still slow, or members randomly log out. Look at X-XF-Cache-Status and at whether context.page exists.
Community Redis add-ons (SV Redis) are extra. Official Redis is already a provider string. Do not stack three Redis opinions.
5. Cookie path / board URL wrong
Official: a $config['cookie']['path'] (or domain) that does not cover the XenForo root means cookies never stick. Everyone looks like a guest. You then enable guest page cache and “logged-in users see the guest homepage.” They were never logged in.
Official connected-accounts test: if the provider test fails, Board URL is the first check. Same string Discord OAuth will need later.
6. Index route vs Home tab vs logo
Setup → Options → Basic board information → Index page route is /. Official examples include forums/ and portal/. Change it and:
- Forum-list widget positions stop running on the first screen (portal already documented this)
- The logo still goes to the index
- Home page URL is a tab. It can point off-site. Logo and tab disagree more often than admins notice
A landing page is a page node you send. It is not a week of index-route theft.
7. Private node, forgot staff
Official private-node rule: Private clears baseline View. You then Yes the groups that may enter. Official premium-forum walkthrough ends with: do not forget Administrative and Moderating. A sold room staff cannot open is not premium. It is an unmoderated bunker.
Parent View still wins. A public child under a private parent is invisible. Every node article on this site has said so. It is still the ticket.
8. Promotions used as a ban-hammer (or as a paid group)
Official promotions: hourly cron, recently active users only, empty criteria never award, disable does not demote, manual demote makes them ineligible forever, lurkers keep old groups until they log in.
Mistakes:
- Empty criteria, then “why is nobody Veteran?”
- Deleting the promotion and expecting the group to vanish (it will not)
- Pointing a promotion at
Subscribersthat a user upgrade also uses - Expecting a lurker to gain a banner overnight
Undo the official way: change criteria to match nobody, or batch-update, or delete the group if it existed only for that promotion.
9. Trophies used as reversible ranks
Official trophies: not revoked when criteria fail. Delete the trophy to pull points from everyone. Cron is Update user trophies. Community also Rebuild trophies.
A birthday trophy, an “has avatar this week” trophy, a “Subscriber” trophy — those are promotions or upgrades. The ranks article is the design. This row is the ticket: “why do they still have the badge?” Because trophies are medals.
10. Permission rebuild at peak
Community: compilation scales with groups × nodes. ~80 nodes × ~60 groups → minutes. ~700 nodes → timeouts. Saving a primary group, a node overlay, and a batch promotion at 20:00 is how a large board “dies.” The moderation shift does not save groups in the first half hour. Join that religion.
11. extra.less / PAGE_CONTAINER / outdated templates
Official: customized templates are not overwritten on upgrade. Appearance → Outdated templates, then merge. PAGE_CONTAINER (and OFFLINE_CONTAINER, OAUTH_CONTAINER) take core updates. Cloning it for a hero is how 2.3’s jQuery removal lands on you as a white screen.
Official 2.3 CSS is unbundled. A novel in global extra.less invalidates the global sheet. Page-only CSS belongs in <xf:css> on that page (widgets / landing page).
Raw content: "\f019" icons miss the Icon Usage Analyzer. Use <xf:fa>. The nodes article already lost that war for you.
12. Cron is dead, so “mail / trophies / promotions are broken”
Deferred work rides cron. Official jobs yield at $config['jobMaxRunTime'] (default 8 seconds). If cron is not hitting job.php, the next visitor is the cron. Pages feel randomly slow. Trophies lag. Promotions lag. Mail piles up.
Tools → Cron entries. If the last-run column is yesterday, fix the server trigger before you install an add-on.
13. Moderate everything
Moderate new threads on the whole tree turns the approval queue into the forum. The 2.3 queue is a page at a time. Hold new-account URLs and spam-trigger hits. Do not hold Introductions “until we hire.”
14. InnoDB / query cache / search index on a tiny VPS
Community database: query cache off (query_cache_type = 0); InnoDB for almost everything; stock MyISAM leftovers include xf_search_index and session tables; converting xf_search_index to InnoDB on < 4 GB RAM can OOM. Official 2.3 floor is MySQL 5.7 / MariaDB, PHP 7.2 (8.3/8.4 recommended). “It works on 7.4” is not a performance strategy.
15. The add-on you installed to hide a mistake
Font Awesome Manager on 2.3 when the Icon Usage Analyzer already subsets. A stats widget when forum_statistics exists. A landing-page product when a page node is a headline and a button. Discord as required login. Credits events on View with no cap.
If the stock tool does the job, the add-on is a second compiler.
Thirty-minute audit
| Check | Where | Healthy |
|---|---|---|
| Primary group | A staff account, a VIP, you | Registered |
debug / fullJs |
src/config.php |
false |
| Gzip | config.php or server |
on, once |
Cache provider + context.page |
config.php |
match what you think you enabled |
| Cookie path / Board URL | config.php / Basic board information |
login survives a refresh |
| Index route | Basic board information | the screen you meant |
| Private nodes | one sold or staff room | mods can View |
| Promotions cron / trophies cron | Tools → Cron entries | ran today |
| Outdated templates | Appearance | empty |
| Cloudflare Auto Minify | CDN | off if XF already minifies |
| Approval queue | public tree | not the whole site |
Three sessions after every “fix”: guest, new Registered, staff. If those three see three boards, you are not done.
What this list is for
Print it. Run it after every upgrade, every add-on, every “quick permission change.” The 2.3 upgrade checklist is the cousin of this one. Together they are most of XenForo operations.
The software is fine. The switch you flipped at 20:00 is not. Put it back. Then measure.
16. Mail, two-step, and “nobody can register”
Official 2.3 mail is Symfony Mailer (the upgrade article). If confirmation mail dies, new accounts stay non-Valid and inherit Unregistered / unconfirmed. It looks like “registration is broken.” It is mail. Test Tools mail / a dummy register before you add CAPTCHA or a spam add-on.
Two-step on admin accounts is not optional on a board that takes payments. Official user-profile docs mention disabling 2FA when someone is locked out — that control is for help, not for you leaving admin without 2FA.
Login CAPTCHA after four failures is official (Setup → Options → User options → Login limit method). Turning it off because you are tired of it is how credential stuffing looks like “members forgot their passwords.”
17. Friendly URLs, /install/, and a leftover install directory
If friendly URLs 404 but index.php?forums/ works, the rewrite layer is the bug, not XenForo. Community SFTP fallback during upgrades still uses /install/ — official 2.3 cannot downgrade; leftover world-readable install dirs after an upgrade are a security smell. Remove them when the upgrade is done.
Board URL with a trailing slash vs without will also break connected accounts and payment return URLs. One canonical. 301 the other.
18. Search that thinks it is Google
Official enhanced search wants Elasticsearch / OpenSearch 7.2+. Turning it on without RAM, or converting xf_search_index to InnoDB on a 2 GB VPS, is a lockup (community). If MySQL FULLTEXT is fine for your size, stay there. A yellow “rebuild search” after an import is a job. Do not start it at peak next to a permission save.
Symptom → first switch
| They say | You check first | Then |
|---|---|---|
| “I’m logged out every click” | Cookie path / Board URL / session cache full | pageCache serving guests to members |
| “The homepage is empty” | Index route / Forums default page | Widgets still on forum_list_* |
| “VIP can’t see their forum” | Primary group; Private node; parent View | Staff View |
| “SQL on the page” | $config['debug'] |
You are done |
| “JS menus dead after CDN” | Auto Minify / script rewrite | fullJs (should be false) |
| “Trophies never award” | Cron last-run | Criteria; they are a lurker |
| “Promotion vanished for some” | Official: disable ≠ demote; lurkers | Batch update vs cron |
| “Slow since last night” | Who saved permissions / rebuilds | Redis down; query cache |
| “Can’t register” | Mail + user state Valid | Spam trigger log |
| “Discord login 400” | Redirect URL + Board URL | Scopes |
Keep this table next to the thirty-minute audit. Most rows are one file or one ACP screen. The add-on store is not on the list.
19. Widgets on a position the style never renders
You added New posts. Nothing shows. The instance is fine. The position is not in the active style — or you flipped Forums default page / Index page route and forum_list_sidebar no longer runs on /. The widgets article is the map: call by key on a page node, or fix the style’s <xf:widgetpos>. Do not clone PAGE_CONTAINER to invent a hole.
A widget that prints $xf.visitor.username inside a guest page cache is the other half of this ticket. Public HTML only in cached slots.
20. Style variations and “dark mode broke extra.less”
2.3 variations are one style, two palettes. Hard-coded #111 in extra.less does not flip. Official xf-intensify / xf-diminish do. After upgrade, test both variations. Outdated PAGE_CONTAINER from a 2.2 dark style is an upgrade merge, not a cache rebuild.
Staging is the last “mistake”
Every row above is cheaper on a clone. Official 2.3 cannot downgrade. Community upgrade practice (disable add-ons, stock style, SFTP /install/) exists because production ACP clicks are how you learn these lessons in public. Copy config.php mistakes to staging first: debug, pageCache context, cookie path. Then copy the working file up.
If you do not have staging, you still have a dummy guest, a dummy Registered, and an hour before peak. Use them. The thirty-minute audit is not a personality test. It is the difference between a configuration error and a status page.
Print the audit. Date it. The next person on staff should be able to run the same thirty minutes without you. Configuration mistakes are rarely clever. They are a true that should have been false, a primary group that should have stayed Registered, a CDN tick that should have stayed off. XenForo 2.3 is large. The dangerous part is how small the wrong checkbox is.
When you find one, write it in the staff node: what it looked like, which switch, who flipped it. That note is worth more than another add-on. It is also how you stop paying for the same outage twice.
A useful habit: after any ACP save that touches groups, nodes, options, or config.php, wait one full guest page load and one logged-in page load before you leave the desk. The mistakes in this article almost all show up on that second load — a logged-out cookie, a missing widget, a SQL footer, a hero that is last month’s feature. If you only test as admin, you will ship admin’s board. Members do not live there.
Keep a throwaway Registered password in the staff locker (not in git, not in a public ticket). Every checklist in this article that says “three sessions” means that account, a private window, and you. If the throwaway cannot post in Introductions after you “fixed permissions,” you did not fix them. You fixed your groups. That distinction is most of XenForo support.
If you want a second pair of eyes after an upgrade, run this article’s audit before you run the speed checklist. A board that is “slow” because debug is on does not need Redis. It needs false. Then measure again as a guest. Only then open config.php for cache.

