Starting with 4.13.3, the default value of trust_proxy will be false on new installs to ensure a secure default setting.

An upgrade script will set the value to true if it is not found in config.json to prevent breaking existing installs.

The documentation is updated to reflect the change:

  • trust_proxy (Default: false) Enables Express' proxy trust handling for req.ip and related values.
    • Only set this to true when NodeBB is behind a reverse proxy that strips or overwrites untrusted X-Forwarded-For headers.
    • If this is left unset, NodeBB will not trust forwarded client IP headers.
    • For more information see expressjs docs https://expressjs.com/en/5x/guide/behind-proxies/

If you are upgrading from 4.13.2 and already use a proxy like nginx, you don't have to do anything. trust_proxy: true will be automatically set in config.json

If your nodebb was facing the public directly on port 4567(which isn't recommended). Remove trust_proxy from config.json OR set it to false via trust_proxy: false.

Harmony Theme Upgrade Changes (community.nodebb.org)
submitted 1 month ago* (last edited 1 month ago) by to c/nodebb-development@community.nodebb.org
 

Organized by change type so upgrades can be done in passes and reviewed more easily.

1. Helper syntax changes ({helper()}{{helper()}})

header.tpl

  • {buildMetaTag}{{buildMetaTag}}
  • {buildLinkTag}{{buildLinkTag}}

Avatar helper (all templates showing user avatars)

  • {buildAvatar}{{buildAvatar}}

Category icon helpers

partials/account/category-item.tpl

partials/categories/item.tpl

partials/quick-category-search-results.tpl

partials/buttons/newTopic.tpl

category.tpl

world.tpl

  • {buildCategoryIcon}{{buildCategoryIcon}}

Category label helpers

partials/posts_list_item.tpl

partials/quick-search-results.tpl

partials/topics_list.tpl

topic.tpl

  • {buildCategoryLabel}{{buildCategoryLabel}}

Other helper conversions

partials/account/session-list.tpl

  • {userAgentIcons(@value)}{{userAgentIcons(@value)}}

partials/topic/post.tpl

  • {generateWrote(@value, config.timeagoCutoff)}{{generateWrote(@value, config.timeagoCutoff)}}

2. Translation (tx()) changes

Account templates

account/categories.tpl

account/uploads.tpl

account/tags.tpl

  • {title}{{tx(title)}}

account/info.tpl

  • {./reason}{{tx(./reason)}}

account/topics.tpl

  • {./name}{{tx(./name)}}

account/posts.tpl

account/shares.tpl

account/topics.tpl

  • {noItemsFoundKey}{{tx(noItemsFoundKey)}}

account/profile.tpl

account/settings.tpl

partials/account/sidebar-left.tpl

partials/categories/link.tpl

partials/skin-switcher.tpl

category.tpl

  • {./name}{{tx(./name)}}

account/settings.tpl

  • {./bootswatchSkinOptions.name}{{tx(./bootswatchSkinOptions.name)}}

  • {./name}{{tx(./name)}}

  • {./label}{{tx(./label)}}

Topic templates

partials/topic/necro-post.tpl

  • {text}{{tx(text)}}

partials/topic/post.tpl

  • {posts.replies.text}{{tx(posts.replies.text)}}

partials/topic/sort.tpl

  • {sortOptionLabel}{{tx(sortOptionLabel)}}

Navigation templates

partials/topic/breadcrumbs.tpl

partials/mobile-nav.tpl

partials/sidebar-left.tpl

  • {./text}{{tx(./text)}}

partials/mobile-nav.tpl

partials/sidebar-left.tpl

  • {./title}{{tx(./title)}}

Other translations

partials/cookie-consent.tpl

  • {message}{{tx(message)}}
  • {link}{{tx(link)}}
  • {dismiss}{{tx(dismiss)}}

notifications.tpl

  • {filters.name}{{tx(filters.name)}}

partials/breadcrumbs-json-ld.tpl

  • {stripTags(./text)}{{tx(./text)}}

3. HTML/raw output, txEscape() changes

Raw HTML rendering

account/profile.tpl

  • {./html}{{./html}}

account/settings.tpl

  • {./content}{{./content}}

partials/topic/post.tpl

  • {posts.user.custom_profile_info.content}{{posts.user.custom_profile_info.content}}

partials/topic/event.tpl

  • {./text}{{./text}}

partials/mobile-nav.tpl

partials/sidebar-left.tpl

  • {./dropdownContent}{{./dropdownContent}}

partials/notifications_list.tpl

  • {./bodyShort}{{./bodyShort}}
  • {./bodyLong}{{./bodyLong}}

txEscape() conversions

account/profile.tpl

  • {aboutmeParsed}{{txEscape(aboutmeParsed)}}

  • {posts.user.signature}{{txEscape(posts.user.signature)}}

partials/topics_list.tpl

  • {./title}{{{ if ./txTitle }}}{{tx(./title)}}{{{ else }}}{./title}{{{ end }}}

  • {./teaser.content}{./teaser.content} => {{txEscape(./teaser.content)}}

partials/topic/post.tpl

  • {posts.content}{{{ if posts.txContent }}}{{tx(posts.content)}}{{{ else }}}{{txEscape(posts.content)}}{{{ end }}}

partials/topic/navigation-post.tpl

  • {post.content}{{{ if post.txContent }}}{{tx(posts.content)}}{{{ else }}}{{txEscape(post.content))}}{{{ end }}}

partials/categories/lastpost.tpl

  • {./content}{{txEscape(./content)}}

partials/posts_list_item.tpl

  • {./content}{{{ if ./txContent }}}{{tx(./content)}}{{{ else }}}{{./content}}{{{ end }}}

4. Remove obsolete escaping

URL escaping

partials/account/header.tpl

  • {escape(cover:url)}{cover:url}

partials/categories/children.tpl

partials/categories/link.tpl

  • {txEscape(./link)}{./link}

partials/topic-list-bar.tpl

  • {escape(url)}{url}

category.tpl

  • {escape(./url)}{./url}

Value escaping

partials/category/tags.tpl

partials/topic/tag.tpl

partials/posts_list_item.tpl

partials/search-filters.tpl

partials/tags_list.tpl

partials/topics_list.tpl

  • {./valueEscaped}{./value}

Strip tags removal

partials/topics_list.tpl

  • {stripTags(./title)}{./title}

category.tpl

  • [[category:handle.description, {txEscape(handleFull)}]][[category:handle.description, {handleFull}]]

5. Translation string syntax updates

partials/sidebar/drafts.tpl

  • [[topic:composer.replying-to, "{txEscape(./title)}"]]{{tx("topic:composer.replying-to", txEscape(quote(./title)))}}

  • [[topic:composer.editing-in, "{txEscape(./title)}"]]{{tx("topic:composer.editing-in", txEscape(quote(./title)))}}

partials/search-results.tpl

  • [[search:results-matching, {matchCount}, {txEscape(search_query)}, {time}]]{{tx("search:results-matching", matchCount, txEscape(search_query), time)}}

6. Topic icon refactor

partials/topics_list.tpl

Replace:

{{{each ./icons}}}<span class="lh-1">{@value}</span>{{{end}}}

With:

{{{ each ./icons }}}

{{{ end }}}

topic.tpl

Replace:

{{{each icons}}}<span class="lh-1">{@value}</span>{{{end}}}

With:

{{{ each ./icons }}}

{{{ end }}}

Add new file

partials/topic/icon.tpl

<span class="badge border {./classes}">
{{{ if ./icon }}}<i class="fa {./icon}"></i>{{{ end }}}
{{{ if ./label }}}<span> {tx(./label)}</span>{{{ end }}}
</span>

7. Structural template updates

topic.tpl

Replace topic meta tags with:


submitted 1 month ago* (last edited 7 hours ago) by to c/nodebb-development@community.nodebb.org
 

Hello Everyone,

The upcoming 4.14.0 release will have breaking changes for themes and plugins that display html and translation tokens via template variables. This post will outline the changes required to custom themes based on the changes made in the Harmony theme to serve as a guide for upgrading your own themes & plugins.

1. Escape all template variables via {variable}

The first and biggest change is the introduction of html and translation escaping of all variables in templates. Prior to 4.14.0 if you wrote {variable} in a template the value would be inserted into the html page unchanged. Benchpressjs supports escaping these variables but it was disabled to prevent a breaking change when the migration was done from templates.js to benchpressjs. We are now aligning with how most template engines work.

What this means for themes is if variable contained html then it will no longer be displayed properly. For example the content of posts is rendered via {posts.content} in partials/topic/post.tpl. Upgrading to 4.14.0 without fixing the template would result in:

blog-1.png

To fix this in 4.14.0, you will have to wrap the variable with double braces like {{posts.content}}. This will stop the escaping and the post content will display properly. Double braces should only be used on content that is html sanitized. In this case NodeBB converts markdown to html and sanitizes it so it is safe. Do not use {{variable}} syntax on untrusted content.

The {{}} syntax should also be used on all helper functions that return html. For example buildAvatar function return html to display the user icon or picture. So {buildAvatar(user, "24px", true)} should be changed to {{buildAvatar(user, "24px", true)}}. We've updated all these helpers to escape the values and attributes used. If you have custom helpers you should do the same. Below is an example of a helper that is used in header.tpl. It uses the same escape function that {variable} uses.

function buildMetaTag(tag) {
  const name = tag.name ? `name="${escape(tag.name)}" ` : '';
  const property = tag.property ? `property="${escape(tag.property)}" ` : '';
  const content = tag.content ? `content="${escape(tag.content).replace(/\n/g, ' ')}" ` : '';

  return '\n\t';
}

Refer to section 3, for a full list of changes that were made to Harmony theme to upgrade it to 4.14.0.

2. Migration to tx() and txEscape() helper functions

This is the second major change, and we're introducing it as part of an ongoing upgrade process to help avoid creating too much work for theme maintainers at once.

The way translation works in NodeBB is through a post render step. After benchpressjs renders the template and generates the html page, our translator scans the html page and replaces translation tokens that look like [[namespace:key]] with their correct translation.

Here is roughly how it goes from request to final html presented to user.

  1. User requests /topic/1
  2. Load all required data from database
  3. Call filter:parse.post to turn post markdown into html
  4. Translator escapes the html from step 3, so tokens in the post content don't get translated
  5. Benchpress renders the html filling in all data
  6. Translator scans html replacing translation tokens with their translations
  7. Translator unescapes the result (reverses step 4)
  8. Final html sent to user

With the introduction of a helper function tx() instead of scanning the entire page, we can use the helper to translate tokens. For example instead of <button>[[topic:reply]]</button> we can use <button>{{tx("topic:reply")}}</button>. This approach is more efficient because only the translation tokens are processed.

With 4.14.0 we will still run the whole page translation to make migration easier, but for 4.15.0 we are going to remove that step, so the entire flow will be:

  1. User requests /topic/1
  2. Load all required data from database
  3. Call filter:parse.post to turn post markdown into html
  4. Benchpress renders the html, calls tx() to translate tokens
  5. Final html sent to user

We are introducing 2 new helper functions for templates:

  • tx() - translates the string or array passed in. This supports a variety of different usages:
Old New
[[namespace:key]] {{tx("namespace:key")}}
[[namespace:key, foo]] {{tx("namespace:key", "foo")}}
[[namespace:key, {arg1}, {arg2}]] {{tx("namespace:key", arg1, arg2)}}
{text} {{tx(text)}}

You can also pass an array to the tx() helper. For example if you have:

const myVar = ["namespace:key", "arg1", "arg2"];

This can be passed straight to tx in the template {{tx(myVar)}}

tx helper html escapes arguments and invalid translation keys.

  • txEscape() - escapes variable so it is not translated. This is a stop-gap solution until we remove the whole page translation. For example we use this on content that should not be translated. If someone puts [[namespace:key]] in their post it doesn't get translated. After 4.15.0 it will be safe to remove these since everything will use tx().

3. List of changes for Harmony Theme

Let's summarize the changes needed for 4.14.0.

  1. Wrap template helper functions that return html with {{}} instead of {}.
  2. Replace {translationString} with {{tx(translationString)}}.
  3. Wrap API values that should not be translated with {{txEscape(content)}}.
  4. (Ongoing) Replace old translation tokens [[namespace:key]] with {{tx("namespace:key")}}.

Refer to the Harmony Upgrade Changes Topic for the full list of changes.

4. Bootbox & app.renderAsync & Misc.

  1. The global bootbox object has been removed, there is a new modals module that also handles translations. Use modals.(dialog|confirm|alert|prompt) instead of bootbox variants. The only difference is these methods return a promise. So the old const modal = bootbox.dialog() becomes const modal = await modals.dialog()

  2. If you are using app.renderAsync to render a template it needs the translation context for tx() helper to work. NodeBB will automatically try to detect the user's language if app.renderAsync is called as part of a web request, but if it is called from a cron job or manually you can pass it the translation data like below:

const languages = nodebb.require('./src/languages);
const data = { 
  foo: 1,
  _i18n: languages.getFull('en-GB'),
};
const html = await app.renderAsync('mytemplate', data);
  1. config.assetBaseUrl was deprecated in 2.x, it is now removed, use config.asset_base_url
Why are we doing this?

As you know, we have a Bug Bounty Program, and the most commonly reported vulnerability is XSS, which is typically caused by unsanitized user input being rendered directly into HTML.

Currently we use the validator module to escape user controlled values however with the introduction of Activitypub support, attackers can manipulate data directly in the database of a remote NodeBB. When this category or user profile is displayed in a target NodeBB instance if any fields are not escaped via validator.escape() then it opens us up for XSS vulnerabilities. By moving the escaping to the template engine, we mitigate this and we no longer have to validator.escape() every value we load from the database.

All these changes are now live on this forum. Please let us know if you notice any issues. We plan to release 4.14.0 in July and follow up with 4.15.0 in August.

Let us know in the Technical Support category if you need help with updating your themes or plugins.

NodeBB 4.12.0 & 3.12.9 Released (community.nodebb.org)
submitted 2 months ago* (last edited 2 months ago) by to c/nodebb-development@community.nodebb.org
 

Hello Everyone 👋,

Today we are releasing NodeBB 4.12.0 and 3.12.9.

These releases contain important security fixes, so upgrading is highly advised. NodeBB 1.x and 2.x no longer receive security updates.

🚨Minimum nodejs version required is 22.x and up.

Along with the security updates there are 4 new features.

Ability to see tags and thumbnails in post queue.

5be91e47-61a2-482a-8b40-69222f09bd5a-image.jpeg

The post will display the tags and thumbnails uploaded, and you can also edit them on the same page. The edit buttons were also moved from the bottom of the card to their respective areas.

Add visual indicators and filter tab for muted users

Muted users were not visible on the /users page and on their profiles. So we added a muted filter on the users page. A badge will also be displayed on their profile page.

Full-text search on chats list

fa732d8c-f196-489c-891e-77ec6fa5eee3-image.jpeg

If you have a lot of chats like @julian, you can now search them on the chat page. 🎊

Allow owners to move misplaced topics

Topic owners can now move their topic if they posted it in a wrong category. There is an admin setting to control the number of posts after this feature is disabled. Thanks to @palmoni for contributing this https://github.com/NodeBB/NodeBB/pull/14227

ESM migration of plugins and core

As part of our efforts to modernize the code base we have added support for writing plugins in ESM. Up until now plugins used require.main.require to import modules from core nodebb. This is now deprecated, the new method to require core modules is to replace it with nodebb.require.

Here is a sample plugin written in ESM that uses the new nodebb.require method https://github.com/NodeBB/nodebb-plugin-esm-test/blob/master/index.js.

Using require.main.require will generate a warning in development mode but will still work. We will slowly update bundled plugins over to ESM and the new method.

Bug fixes & Misc 🐛

Full list of closed issues.

 

We have released NodeBB versions v2.8.20, v3.12.7, and v4.4.4 to address a security issue.

If you discover a vulnerability, please report it responsibly via our bug bounty program.

As mentioned before, we are going to support 2.x up to August 2025.

 

We have released NodeBB versions v2.8.19, v3.12.6, and v4.4.3 to address a security issue involving a potential XSS vector.

If you discover a vulnerability, please report it responsibly via our bug bounty program.

As mentioned before, we are going to support 2.x up to August 2025.

 

We have released NodeBB versions v2.8.18, v3.12.5, and v4.3.2 to address a security issue involving a potential XSS vector and a SQL injection vulnerability. We strongly recommend all users update to the latest patch version for their respective branches:

If you discover a vulnerability, please report it responsibly via our bug bounty program.

As mentioned before, we are going to support 2.x up to August 2025.

submitted 2 years ago* (last edited 4 months ago) by to c/general-discussion@community.nodebb.org
 

You can go to your theme settings and enable a new option to adjust the layout of the topic page.

One thing that always bothered me in Persona (and to some extent in Harmony) was how far down the main post appeared on the page. This new option helps alleviate that issue. By default, the post bar remains at the top, and the mobile view remains unchanged.

Here’s a screenshot:

f403770d-3ea0-4483-a0c0-528021192040-image.png

For reference this is the original layout

50d76d4e-939a-4126-bb61-478cf0c0a80d-image.jpeg

view more: next ›