Use WP Discourse to publish posts from Wordpress to Discourse

You can use the WP Discourse Plugin to Publish Wordpress Posts to your Discourse so your community can talk about your Wordpress content. Before you can set up Publishing you first have to install the WP Discourse plugin on Wordpress and connect it to your Discourse. If you’re ready to get started, start by watching this short video, or follow the instructions below.

Next Step

Once you’ve set up publishing, you may want to check out the following topics


Publishing Settings

:warning: Make sure you always save your settings after changing them. Click the ‘Save Options’ button at the bottom of the page.

The settings should be relatively self-explanatory. If you’re having an issue understanding any of them please reply to this topic for a further clarification.

Default Discourse Category

Sets the default category in which your posts will be published on Discourse. This setting can be overridden for individual posts on the WordPress post-new screen.

Display Subcategories

Indicates whether or not you want your forum’s subcategories to be available as categories that you can publish to from WordPress. You will need to save this setting before subcategories become available in the Default Discourse Category option input.

Force Category Update

For when you have added new categories to your forum and would like them to be available on your WordPress site. Enabling this setting and saving the options page makes a single API call to Discourse to retrieve the Discourse categories. After enabling it, the next time you navigate back to the Commenting tab, you will find the setting disabled.

Allow Tags

Enable this if you would like to add tags to Discourse topics that are created via WordPress.

Publish as Unlisted Topics

Will cause posts published from WordPress to be unlisted on Discourse. If you enable the Sync Comment Data webhook setting, unlisted posts will be made visible when they first receive a reply on Discourse.

Use Full Post Content

Let’s you publish full WordPress posts, rather than excerpts, to your Discourse forum. To keep the Show Full Post button from appearing under your post on Discourse, you must un-select the embed truncate site setting on Discourse (found at

Custom Excerpt Length

If you have not selected the Use Full Post Content setting, this setting will create excerpts of that length to be published to Discourse. You can also manually create excerpts when you create a WordPress post by adding the excerpt to the Excerpt meta-box.

Auto Publish

This pre-check’s the Publish to Discourse checkbox that appears on the post-new screen for post-types that are to be published to Discourse. The checkbox can still be un-checked when creating the post.

Auto Track Published Topics

This setting is enabled by default. When enabled, the author of a post published to Discourse from WordPress will automatically be ‘Watching’ the topic (they will receive Discourse notifications about every new reply)

Post Types to Publish

This setting must be set. It defaults to post, but pages and custom post types may also be selected.

Exclude Posts By Tag

If you add Wordpress tags to this setting, any Wordpress post with one of the tags will not be published to Discourse.

Do Not Display Discourse Name Field

This will hide the “Discourse Username” field in user’s profiles. The Discourse Username is used to set the author of the topic when publishing posts to Discourse.

Discourse Username Editable

This determines whether non-admins are able to edit their own Discourse Username in their profile. The Discourse Username is used to set the author of the topic when publishing posts to Discourse.

Direct Database Publication Flags

This setting is used in certain setups that have specialized Wordpress environments. Don’t use this setting unless you know what it does.

Verbose Publication Logs

Enabling this setting will mean that all posts published to Discourse are logged in the WP Discourse logs, even if they succeed. Normally only errors are logged.

Enable posting by XMLRPC

XML-RPC is a remote procedure call that is often used by blogging software for sending posts to WordPress. Apps that use this procedure include the blogging app.

By default, WP Discourse will not publish posts to Discourse that are created through XML-RPC. The reason for this is that there is no great way to indicate whether a post published through blogging software is meant to be published on Discourse.

If you would like to use blogging software for creating posts that are directly published to Discourse, you need to add some code to your theme’s functions.php file that hooks into the wp_discourse_before_xmlrpc_publish filter. The wp_discourse_before_xmlrpc_publish filter passes two arguments to functions that hook into it. The first argument, $publish_to_discourse is a boolean that is set to false to disable publishing by XML-RPC. The second argument is the post object.

To have all XML-RPC posts automatically published by Discourse you need to write a function that will always return true. Use something like the following code:

Warning: this will cause all posts published through XML-RPC to be published to Discourse, this will include old posts that are edited on blogging software.

add_filter('wp_discourse_before_xmlrpc_publish', 'my_namespace_xmlrpc_publish', 10, 2 );
function my_namespace_xmlrpc_publish( $publish_to_discourse, $post ) {
  return true;

Filtering XML-RPC posts by post tag

The blogging app allows you to add tags to posts. Tags can be used to control whether or not a post is published to Discourse. To only publish posts that have a ‘discourse’ tag, use something like this:

add_filter('wp_discourse_before_xmlrpc_publish', 'my_namespace_xmlrpc_publish_by_tag', 10, 2 );
function my_namespace_xmlrpc_publish_by_tag( $publish_to_discourse, $post ) {
  if ( has_tag( 'discourse', $post ) ) {
    return true;
  return false;

If you would like to use this method, but not have the discourse tag appear in your published posts, you can remove it with some code like this:

add_filter( 'term_links-post_tag', 'my_prefix_remove_discourse_tag' );
function my_prefix_remove_discourse_tag( $tags ) {
    foreach( $tags as $key => $value ) {
        if ( strpos( $value, 'discourse' ) ) {
            unset( $tags[ $key ] );

    return $tags;

Filtering XML-RPC posts by post date

To only allow posts published after a certain date to be published to Discourse through XML-RPC, add some code like this to your functions.php file. This code will allow all posts published after January 1, 2016 to be published to Discourse.

add_filter('wp_discourse_before_xmlrpc_publish', 'my_namespace_xmlrpc_publish_by_date', 10, 2 );
function my_namespace_xmlrpc_publish_by_date( $publish_to_discourse, $post ) {
  if ( strtotime( $post->post_date ) > strtotime( '2016-01-01') ) {
    return true;
  return false;

Todo: add a filter to the wp-discourse plugin to allow for only publishing new posts (as opposed to edited posts) through XML-RPC.

Filtering the available categories for a custom post type

Before the Discourse categories are displayed in the Publish to Discourse meta-box on the Wordpress admin/post-new page, the Wordpress filter 'wp_discourse_publish_categories' is applied to them. It is given the Discourse categories array, and the current post as arguments. This can be used in your theme’s functions.php file to limit which categories are available for a given post type.

Here is an example that creates an 'acme_product' post type and then returns the Discourse categories ‘fun’ and ‘scratch’ to be displayed in the Publish to Discourse meta-box for posts of that type.

// Create the post type.
add_action(  'init', 'my_namespace_create_post_type' );
function my_namespace_create_post_type() {
  register_post_type( 'acme_product',
      'labels' => array(
      'name' => __( 'Products' ),
      'singular_name' => __( 'Product' )
    'public' => true,
    'supports' => array( 'title', 'editor', 'comments', 'custom-fields' ),
    'has_archive' => true,
    'show_in_rest' => true,

// Filter the available categories for the 'acme_product' post type.
add_filter( 'wp_discourse_publish_categories', 'my_namespace_filter_categories', 10, 2 );
function my_namespace_filter_categories( $categories, $post ) {
   if ( 'acme_product' === get_post_type( $post ) ) {
     $output = [];
     foreach ( $categories as $category ) {
        if ( 'fun' === $category['name'] || 'scratch' === $category['name'] ) {
          $output[] = $category;
     return $output;
   return $categories;

If you want to strictly apply a specific category to specific post types (i.e. 1:1) then you should use wpdc_publish_post_category instead.

function wpdc_change_post_category( $category, $post_id ) {
  if ( 'acme_product' === get_post_type( $post ) ) {
       $categories = WPDiscourse\Utilities\Utilities::get_discourse_categories();
       $fun_category = array_search( 'fun' , array_column( $categories, 'name' ));
       return $fun_category;
  } else {
       return $category;
add_filter( 'wpdc_publish_post_category', 'wpdc_change_post_category' );

Display metadata of a connected Discourse topic

When you have Publishing set up you can display metadata about the Discourse topic by using Template Customisation.

First, you need to decide which template you wish to modify. If, for example, you only wish to show topic metadata, and no replies as comments, then you’ll want to modify the no_replies template.

Once you’ve chosen a template, you can modify it to add Discourse topic metadata. Here’s a snippet that does that, with some comments explaining what the code is doing

// ensure wp-discourse is present using your preferred method,
// e.g.

// Require the plugin-utilities from the wp-discourse plugin
require_once ABSPATH . 'wp-content/plugins/wp-discourse/lib/plugin-utilities.php';
use WPDiscourse\Shared\PluginUtilities;

// Encapsulate your topic metadata template in a class so you can easily include the plugin utilities
class DiscourseTopicMetadata {
  // Include the wp plugin utilities in your class
  use PluginUtilities;
  public function __construct() {

    // Add the template filter on the initialization of the class
    add_filter( 'discourse_no_replies_html', array( $this, 'topic_metadata_html' ) );

  function topic_metadata_html( $input ) {
    // Get the discourse permalink from the post metadata
    $discourse_permalink = get_post_meta( get_the_ID(), 'discourse_permalink', true );

    // Use the discourse permanlink to get the topic JSON from Discourse, using the helper method from the plugin utilities which handles authorization for you
    $topic = $this->get_discourse_topic($discourse_permalink);
    // Output your markup, including which topic metadata you wish to display
    <div id="discourse-topic-meta">
        <div class="views">
          <?php echo $topic->views; ?>
        <div class="replies">
          <?php echo $topic->reply_count; ?>
    return ob_get_clean();

// Instantiate the class to add the filter
new DiscourseTopicMetadata();

For more details on what topic metadata is available in the response from get_discourse_topic see Discourse API Docs.


Can previously posted blog posts be imported? For example blog posts from within the last 30 days? Just to get that category populated a bit. Thanks.

1 Like

There isn’t a bulk import option at the moment. You can still achieve this by editing each of the older posts and publishing them to Discourse.


I’m setting my sites so that only a few people log in to WordPress (authors), but Discourse is pretty much open to anyone. Discourse is used for authentication.

I would like for the topics created from articles to show the Discourse user’s name instead of “System” (or whatever I have the Publishing Username set to).

In other words, Publishing Username is set to “System”. Joe Blow posts a new article in WordPress. Joe Blow exists in Discourse (since Discourse controls authentication, all WordPress users are also Discourse users). I would like the topic to show as posted by Joe Blow instead of System. Later, on the same sites, Jane Doe posts an article, I would like the topic in Discourse to show as posted by Jane Doe instead of System.

Is this doable?

I don’t know. But if you check out tab Publish and there the last option Single User API Key Publication — that is something that I would try first.

If you are using DiscourseSSO (or what ever connect it is or was…) then nobody can’t registrate to your forum without going via WordPress. So, if WordPress controls account of editors on Discourse how would you do that without demanding first registration on WordPress for everyone? I’m curious because I could use something like that.

(And… I’m on off topic again :man_facepalming:)

I used the DiscourseConnect client setting. That uses Discourse as the authenticating system, and creates the user in Wordpress if they don’t exist. From the instructions on the plugin.

Enabling your site to function as a DiscourseConnect client allows WordPress user authentication to be handled through either your Discourse forum, or your WordPress site. If a Discourse user logs into WordPress through a DiscourseConnect link, they will be authenticated based on their Discourse credentials. If that user doesn’t yet exist on your WordPress site, a new user will be created.

Users will initially be set as subscribers in WordPress, and I will manually set them to authors (as needed). I plan on setring the site up so that unregistered users will be able to see all content, and will really only need to register in WP of they want to post articles.

All users in WordPress will first have to register in Discourse, then the connector will automatically either log them into WP, or create an account then log them in.

Yes it does, but overdrives all other ways authenticate, and then a normal user of forum must registrate on WordPress before get access to Discourse.

Or am I misunderstanding something?

From what I’ve seen, a regular Discourse user never has to go to the WP site. If they decide to follow a link from the Discourse site, they automatically get registered on the WP site.

You’re welcome to look at the two sites ti see what I’m talking about. Keep in mind, they’re not complete yetx but that part works now.
Https:// is the Discourse
Https:// is the WP site.

Create an account on the WP site. You’ll able to see, and reply to, the messages there.

Then go to the WP site, and select the Login menu item. It will bypass the WP login, and instead connect you to the Discourse login (depending on your settings). If you’ve selected the option to stay logged in, it won’t even prompt you. It’ll immediately go back to the WP site after logging you in.

You just need to make sure that the users posting the articles on Wordpress have their Discourse Username filled in the user’s profile. If the user has a Discourse Username posts they make in Wordpress will be published to Discourse using their username.

You can update this manually, or update it automatically using the Update Userdata webhook (see WP Discourse “Webhooks” settings).


Before I go an manually edit all 393 published posts on my WP, I just wanted to double check this if this is still the case? i.e. that there is still no bulk import option? Thanks!

There’s no bulk import option. Keep in mind that if you publish 393 posts to Discourse you’ll have 393 topic stubs. I would advise you consider a more targeted strategy. What is your community actually going to be discussing now?


My guess is they aren’t. Discourse is just used to replace commenting, nothing else. So, it is not for Discourse, but for WordPress.

I was on similar situation. Because most of world’s WordPress posts aren’t catching any kind of comments, or those are more or less just noise, mine was no different. But instead working with all content, I was using my old WP-content to waking up activity on Discourse. I didn’t connect all posts at once, but dripping. Like now there is active topics of heatstroke in dogs and how many calories you should eat on trail — old posts, that got new life on Discourse.

If there would be an option to connect everything as bulk, I would do it — without understanding such missed potential. I could even say, that burry bulk option six feet under even you could code it :wink:


I’m just trying to get all the comments into Discourse before I migrate the website from WordPress to Ghost as Ghost doesn’t do comments (although you can connect it to Discourse for comments).

But it’s just occurred to me now that if I first connect my new Ghost to my Discourse before importing all my WordPress posts that might do it… maybe (/me goes off to give that a try…)

It doesn’t migrate comments from WP to Discourse. It just shows Discourse posts in WordPress.

If I understood right…

1 Like

Yeah, there isn’t any point in linking Wordpress posts and Discourse topics if you’re about to migrate to Ghost.

Hello everyone!

I just completed the configuration of my community with Discourse! Currently testing the WP Discourse plugin on my staging site.

I have a concern about potential SEO issues. Specifically, by publishing the same content from WP to Discourse, I am concerned about duplicating content as per SEO issues. Does Discourse mark the content in the forum as a copy for SEO purposes?

Hello Victor :slight_smile:

You should be fine SEO wise with the default settings (as they’re different domains), but you can also set the canonical URL of the Discourse topic to be the Wordpress post url.

Enable the Discourse site setting embed set canonical url, then the WP post permalink will be the canonical url of the discourse topic (i.e. it will appear in the head of the Discourse topic HTML as the cannonical link).

For more context on this see

1 Like

Thanks @angus. I really appreciate this. For me, it is safer to take control with SEO, than leave it to chance!

1 Like

I am trying to set different categories for different custom_post_types, the code below selects the correct category in the Wordpress admin. However, on publishing to Discourse, the category isn’t set there (and defaults to uncategorised)…

Maybe I am missing something obvious?

// WP Discourse set CPT forum categories for CPTS
add_filter( 'wp_discourse_publish_categories', 'radhr_filter_categories_policies', 10, 2 );
function radhr_filter_categories( $categories, $post ) {
	if ( 'policy' === get_post_type( $post ) ) {
		$output = [];
		foreach ( $categories as $category ) {
			if ( 'Policies' === $category['name']) {
				$output[] = $category;
		return $output;
	elseif ( 'guide' === get_post_type( $post ) ) {
		$output = [];
		foreach ( $categories as $category ) {
			if ( 'Guides' === $category['name']) {
				$output[] = $category;
		return $output;
	elseif ( 'post' === get_post_type( $post ) ) {
		$output = [];
		foreach ( $categories as $category ) {
			if ( 'Blogs' === $category['name']) {
				$output[] = $category;
		return $output;

	return $categories;

You made a reasonable assumption about how to achieve your goal, but wp_discourse_publish_categories won’t work reliably if you’re filtering to only one category with gutenberg editor.

You should use wpdc_publish_post_category instead:

I’ll update the OP to note that.

1 Like