Serve Discourse from a subfolder (path prefix) instead of a subdomain

:warning: Discourse Official Statement about Subfolder setup

We support subfolder setups for our hosted customers at the enterprise level and up. Due to heavy technical setup complexity we strongly recommend you do not use this setup unless you are very experienced in custom subfolder setups.

It is critical you have a deep understanding of

  • NGINX setup in the Discourse Docker container
  • Secure original IP forwarding using custom headers in the proxy chain
  • Rate limiting in front proxy server

If this all sounds odd and strange to you we strongly recommend you avoid this setup.

To serve Discourse from a subfolder (a.k.a. with a path prefix) on your domain, like https://www.example.com/=SUBFOLDER=, here’s how to do it!

Docker config

In the env section of your docker container yml file, add the DISCOURSE_RELATIVE_URL_ROOT setting with the subfolder you want to use. Make sure it does not end with a /.

Editing this will update the entire guide.

env:
  ...
  DISCOURSE_RELATIVE_URL_ROOT: /=SUBFOLDER=

The run section needs some changes to send all Discourse routes to the right place. Here is a complete run section with subfolder support:

run:
    - exec:
        cd: $home
        cmd:
          - mkdir -p public/=SUBFOLDER=
          - cd public/=SUBFOLDER= && ln -s ../uploads && ln -s ../backups
    - replace:
       global: true
       filename: /etc/nginx/conf.d/discourse.conf
       from: proxy_pass http://discourse;
       to: |
          rewrite ^/(.*)$ /=SUBFOLDER=/$1 break;
          proxy_pass http://discourse;
    - replace:
       filename: /etc/nginx/conf.d/discourse.conf
       from: etag off;
       to: |
          etag off;
          location /=SUBFOLDER= {
             rewrite ^/=SUBFOLDER=/?(.*)$ /$1;
          }
    - replace:
         filename: /etc/nginx/conf.d/discourse.conf
         from: $proxy_add_x_forwarded_for
         to: $http_your_original_ip_header
         global: true

$http_your_original_ip_header stands for Your-Original-Ip-Header, which is a trusted Header you set on the origin which contains the actual client IP.

This is required cause traffic passes through a central proxy, if Discourse happens to have a public IP you can spoof it. If Discourse is private you may be able to get away with X-Forwarded-For

After making these changes, bootstrap your Docker container as usual, or rebuild if you’re changing an existing container.

./launcher bootstrap app

or

./launcher rebuild app

Attached is a complete example yml file of a standalone container.

subfolder-sample.yml (3.1 KB)

Rate limiting concerns

If you are going for this setup you probably want to rate limit requests prior to them landing on NGINX in the container, that means you probably will avoid using our rate limiting template. It is very hard to configure NGINX in the container to limit on a remapped IP and would require complex changes to the template.

Existing posts

If you did this with an existing site that was on a subdomain, you’ll find that your uploads are broken. There’s a tool that can help fix all paths to include the subfolder. In the Discourse directory (typically /var/www/discourse'), run it like this after taking a backup:

RAILS_ENV=production bundle exec script/discourse remap '/uploads' '/=SUBFOLDER=/uploads'

See also: Discourse in a subfolder, multiple servers sharing a domain for more esoteric setups.

robots.txt

Now that Discourse is running on a subfolder, it cannot serve its robots.txt file to control which routes are crawled by web crawlers. Crawlers will be looking at your main site’s robots.txt file (https://www.example.com/robots.txt). You need to copy the contents of Discourse’s robots.txt file (found at https://www.example.com/=SUBFOLDER=/robots.txt) and put it in your main site’s robots.txt file.

33 Likes