Redirect old forum URLs to new Discourse URLs using permalinks

:bookmark: This reference explains how Discourse permalinks redirect old URL paths to new destinations, how permalink normalizations work, and how to test and troubleshoot redirects after a migration or URL structure change.

:person_raising_hand: Required user level: Administrator

:computer: Console access required only when creating permalinks from an importer, script, or Rails console

When you migrate from another forum or change your site’s URL structure, old links from search engines, bookmarks, emails, or other websites may no longer point to the correct page.

Permalinks let you redirect old URL paths to their new destinations using 301 Moved Permanently redirects.

Permalinks are exact path mappings. For example, if this old URL no longer works:

https://discourse.example.com/forum/topic/123

you can create a permalink so it redirects to the new topic:

https://discourse.example.com/t/welcome-to-our-community/456

What permalinks are

A permalink is an exact mapping from one old URL path to one new destination.

For example:

forum/topic/123 β†’ /t/welcome-to-our-community/456

Permalinks are useful when:

  • you migrated from another forum platform
  • old links are indexed by search engines
  • old links are used in emails, documentation, or other websites
  • you changed the structure of your site’s URLs
  • you want an old path to redirect to a topic, post, category, tag, user, or external URL

Each permalink URL maps to one destination.

Permalinks are exact matches. The permalink URL field does not support wildcards or regular expressions. If many old URLs follow a predictable pattern, use permalink normalizations to transform incoming URLs before the exact permalink lookup.

Permalinks and normalizations

Permalinks and normalizations do different things:

Feature What it does Example
Permalink Redirects one saved old path to one destination old/topic/123 β†’ Topic 456
Normalization Changes the incoming path before permalink lookup old/topic/123-title β†’ old/topic/123

A normalization does not redirect by itself. It only changes the path that is used to look up a saved permalink.

You still need a matching permalink after the normalization is applied.

Quick examples

Redirect an old topic URL

Old URL:

https://discourse.example.com/forum/topic/123

Permalink URL:

forum/topic/123

Destination type:

Topic

Destination:

456

Result:

/forum/topic/123 β†’ /t/welcome-to-our-community/456

Redirect an old URL with a query string

Old URL:

https://discourse.example.com/viewtopic.php?t=123

Permalink URL:

viewtopic.php?t=123

Destination type:

Topic

Destination:

456

Result:

/viewtopic.php?t=123 β†’ /t/welcome-to-our-community/456

Redirect many old URLs with the same pattern

Old URLs:

/forum/support/123-how-do-i-reset-my-password.html
/forum/general/456-how-do-i-change-my-email.html

You can use a normalization to simplify them before lookup:

/forum/support/123-how-do-i-reset-my-password.html β†’ forum/123
/forum/general/456-how-do-i-change-my-email.html β†’ forum/456

Then create exact permalinks:

forum/123 β†’ Topic 1001
forum/456 β†’ Topic 1002

What to enter as the permalink URL

Enter the old path, not the full URL.

For example, use:

forum/topic/123

or:

/forum/topic/123

Do not use the full old URL:

https://discourse.example.com/forum/topic/123

Leading slashes are accepted, but they are removed when the permalink is saved. These two entries are equivalent:

/forum/topic/123
forum/topic/123

Both are stored as:

forum/topic/123

Leading and trailing whitespace is stripped when the permalink is saved.

Query strings are part of the match

Permalinks match the full request path, including the query string.

That means these two URLs are different permalink matches:

/old/topic/123
/old/topic/123?utm_source=example

If the old URL has a query string, either:

  1. create a permalink that includes the query string, or
  2. use a permalink normalization to remove or simplify the query string before matching

For most analytics or tracking parameters, using a normalization is usually better than creating many separate permalinks.

For example, this saved permalink:

docs/123

does not necessarily match this requested URL:

/docs/123?utm_source=newsletter

unless a normalization removes the query string before lookup.

When copying old URLs from analytics tools, search console reports, browsers, or email campaigns, check whether tracking parameters have been added.

URL fragments are not part of the match

URL fragments are not sent to the server.

For example, this URL:

/old/topic#post-12

arrives at the server as:

/old/topic

A permalink or normalization cannot match:

#post-12

unless the # is encoded in the actual request as %23.

Supported permalink destinations

A permalink can redirect to one of these destination types:

  • Topic
  • Post
  • Category
  • Tag
  • User
  • External or relative URL

External or relative URL destinations can be used for redirects such as:

old/privacy-policy β†’ https://archive.discourse.example.com/privacy

or:

old/preferences β†’ /my/preferences

Use external URL destinations carefully. They redirect visitors away from your site and do not check whether the target URL exists.

Prefer internal destination types when redirecting to migrated topics, posts, categories, tags, or users. Use an external URL destination when the target page is not represented by an internal object.

Add a permalink manually

To add a permalink manually:

  1. Go to /admin/config/permalinks.
  2. Select the Permalinks tab.
  3. Click Add permalink.
  4. Enter the old path in the URL field.
  5. Select the Permalink type.
  6. Enter or select the destination.
  7. Save the permalink.
  8. Test the old URL in a browser or with curl.

Example:

URL:
forum/topic/123

Permalink type:
Topic

Destination:
456

A request to:

https://discourse.example.com/forum/topic/123

will redirect to topic 456.

Destination values

The destination value depends on the permalink type.

Permalink type Destination
Topic A topic
Post A post
Category A category
Tag A tag
User A user
External or relative URL A full external URL or a relative path

Examples:

forum/topic/123 β†’ Topic 456
forum/post/789 β†’ Post 789
forum/category/support β†’ Category support
forum/tag/example β†’ Tag example
forum/user/alice β†’ User alice
old/privacy β†’ https://archive.discourse.example.com/privacy
old/preferences β†’ /my/preferences

Creating permalinks during a migration

Many official importers create permalinks automatically for migrated categories, topics, posts, or users.

For large migrations, permalinks are usually created by the importer or by a migration script rather than entered manually one at a time.

If you are writing a custom importer, create permalink records for old URLs that should continue to work after the migration.

For example:

Permalink.create!(url: "discussion/12345", topic_id: 987)

In this example, discussion/12345 is the old path, and 987 is the Discourse topic ID.

A leading slash is also accepted:

Permalink.create!(url: "/discussion/12345", topic_id: 987)

Both examples store the permalink URL as:

discussion/12345

To redirect to an external URL:

Permalink.create!(
  url: "discussion/12345",
  external_url: "https://archive.discourse.example.com/discussion/12345"
)

When creating permalinks in an importer:

  • store the old path, not the full old domain
  • include the query string only if it is required for matching
  • make sure each old path maps to one destination
  • check for duplicates before inserting records
  • remember that permalink URLs must be unique

If you run multiple test migrations, final topic and post IDs may change between runs. Generate or verify your final permalink mappings when the production migration data is stable.

What permalink normalizations are

A permalink normalization changes an incoming old path before looking for a matching permalink.

Normalizations are useful when many old URLs follow the same predictable pattern.

For example, suppose your old forum used URLs like this:

/forum/support/123-how-do-i-reset-my-password.html

but your saved permalink is simpler:

forum/123

A normalization can transform the incoming request into the saved permalink path:

/forum/support/123-how-do-i-reset-my-password.html
        ↓
forum/123

Then the permalink can redirect:

forum/123 β†’ /t/how-do-i-reset-my-password/456

A normalization does not redirect by itself. It only changes the path used for the permalink lookup.

You still need a matching permalink for the normalized path.

Most sites do not need permalink normalizations. Use them only when many old URLs follow a predictable pattern and creating one permalink per old URL would be impractical.

How permalinks and normalizations work together

When an old URL is requested, the process is:

Visitor requests old URL
        ↓
Permalink normalizations are applied, if configured
        ↓
A permalink lookup is performed using the resulting path
        ↓
If a matching permalink exists, the visitor is redirected

For example:

Requested URL:
/old/topic/123?utm_source=newsletter

Normalization changes it to:
old/topic/123

Permalink redirects it to:
/t/new-topic-title/456

Add a permalink normalization

Permalink normalizations are configured from the permalink settings.

To add one:

  1. Go to /admin/config/permalinks.
  2. Select the Settings tab.
  3. Add a rule to the permalink normalizations setting.
  4. Save the setting.
  5. Test an old URL that should be normalized.
  6. Confirm that the normalized path matches an existing permalink.

The syntax is:

/<regex>/<replacement>

Multiple rules are separated with |.

For example:

/old\/topic\/(\d+).*/topic/\1

This can transform:

old/topic/123-my-old-title

into:

topic/123

The replacement uses Ruby replacement syntax, so capture groups are written as:

\1
\2
\3

Important normalization rules

Keep normalization rules simple.

  • Normalizations use Ruby regular expressions.
  • Literal / characters in the regex part must be escaped as \/.
  • Literal / characters in the replacement part do not need to be escaped.
  • Multiple normalization rules are separated with |.
  • Because | separates rules, avoid using regex alternation with |.
  • Each rule uses a single replacement, not a global replacement.
  • Rules are applied in order.
  • Matching rules are applied sequentially.
  • Normalizations are applied before permalink lookup.
  • Normalizations are also applied when saving permalink records.
  • The setting validator catches invalid regular expressions, but it may not catch every rule that behaves differently than intended.

:warning: Warning: Normalizations are applied when permalink records are saved. If you enter a non-normalized URL into the admin UI, it may be stored as the normalized result. If you add or change normalizations after creating permalinks, test carefully and check how newly saved permalink URLs are stored.

Normalization examples

Redirect an old URL with extra title text

Old URLs:

/forum/support/123-how-do-i-reset-my-password.html
/forum/general/456-how-do-i-change-my-email.html

You can normalize them to simpler permalink paths:

forum/123
forum/456

Example normalization:

/forum\/[^\/]+\/(\d+).*/forum/\1

Then create permalinks:

forum/123 β†’ Topic 1001
forum/456 β†’ Topic 1002

Ignore tracking query strings

Old requested URL:

/docs/123?utm_source=newsletter

Saved permalink:

docs/123

Normalization:

/(docs\/\d+)\?.*/\1

This removes the query string before permalink lookup.

Preserve an important query parameter

Some old forums use query parameters as the stable topic identifier.

For example:

viewtopic.php?f=10&t=123
viewtopic.php?t=123

You may want to normalize these to:

viewtopic.php?t=123

Example normalization:

/(viewtopic\.php\?)(?:.*&)?(t=\d+).*/\1\2

Then create a permalink:

viewtopic.php?t=123 β†’ Topic 456

Regex and wildcard redirects

The permalink URL field does not support regexes or wildcards.

This will not work as a permalink URL:

forum/topic/*

This will not work as a permalink URL either:

forum/topic/(\d+)

If you need pattern-based handling, use a permalink normalization to rewrite the incoming URL to a form that can match an exact saved permalink.

For example:

/forum/topic/123-title β†’ forum/topic/123

Then create a normal exact permalink:

forum/topic/123 β†’ Topic 456

Route precedence and built-in paths

Permalink routing is checked after normal application routes.

This means a valid existing route may resolve normally before a permalink is checked.

Be careful with old URLs that begin with built-in paths such as:

/t
/c
/u
/tag
/tags

For example, an old forum URL like this may look like a built-in category route:

/c/blog/old-platform-url/ba-p/12345

If an old URL conflicts with a valid existing route, the valid route can take precedence over the permalink.

Similarly, if you create a permalink for a path that looks like an existing topic route, such as:

t/123

the normal topic route may be handled before the permalink lookup.

Some built-in not-found routes have additional fallback behavior, but you should not rely on that. Always test old URLs that overlap with built-in paths.

If you need an old topic URL to redirect somewhere else, the old topic generally must not continue to resolve as a valid topic route.

Permissions and private content

Permalinks to internal destinations respect normal permissions.

If a permalink points to a private or restricted topic, post, category, tag, or user, visitors who cannot access that destination will get a 404 instead of a redirect.

Permalinks to external URLs do not check internal content permissions.

Special characters and encoded URLs

Test URLs with special characters carefully.

This includes URLs with:

  • spaces
  • +
  • %
  • &
  • '
  • :
  • parentheses
  • non-Latin characters

Permalink matching uses the encoded request path after removing the leading slash and applying configured normalizations. Encoding differences can prevent a URL from matching the permalink you expect.

For example, these may not be equivalent depending on how the old URL is requested and stored:

old/topic/hello%20world
old/topic/hello+world

Use %20 for spaces in URL examples. In a URL path, + is not the same as a space.

Characters such as &, ?, and # have special meaning in URLs. If they are intended to be literal path characters, they should be percent-encoded.

When in doubt, test the exact old URL that users or search engines will request.

Limitations

Permalinks are designed for redirecting old paths to new destinations. They are not a general-purpose redirect or rewrite engine.

Important limitations:

  • Permalink URLs are stored as path and query strings, not full URLs.
  • Full URLs are not automatically converted to paths.
  • Permalink URLs must be unique after normalization.
  • A permalink URL can have only one destination.
  • Supported destinations are limited to topics, posts, categories, tags, users, and external or relative URLs.
  • Permalinks use 301 Moved Permanently; there is no per-permalink option for 302.
  • The permalink URL field does not support wildcards or regular expressions.
  • Query strings are part of the lookup key.
  • URL fragments, such as #post-12, are not sent to the server and cannot be matched.
  • Matching does not use the request scheme or host.
  • Native routes are checked before the catch-all permalink route.
  • Internal destinations are permission checked.
  • External URL destinations bypass internal content permission checks.
  • Permalink normalizations use Ruby regular expressions.
  • Normalization rules are separated with |, so avoid using | as regex alternation.
  • Normalization rules use a single replacement per rule, not a global replacement.
  • Normalizations are applied both before lookup and before saving permalink records.
  • The setting validator catches invalid regular expressions, but it may not catch every rule that behaves differently than intended.

For most sites, simple one-to-one permalinks are easier to maintain than complex normalization rules. Use normalizations only when old URLs follow a predictable pattern.

Avoid redirecting every old URL to the homepage

Do not redirect all old URLs to your homepage.

Redirect each old URL to the most relevant new page. If there is no equivalent content, a 404 may be better than sending users and search engines to an unrelated page.

Good redirects are specific:

old/topic/123 β†’ new topic about the same subject

Poor redirects are generic:

old/topic/123 β†’ homepage
old/topic/456 β†’ homepage
old/topic/789 β†’ homepage

Test your redirects

After creating permalinks or normalizations, test a sample of old URLs.

You can test in a browser, or use:

curl -I https://discourse.example.com/forum/topic/123

A working permalink redirect should return a permanent redirect similar to:

HTTP/2 301
location: https://discourse.example.com/t/welcome-to-our-community/456

Test examples from each old URL pattern, including:

  • old topic URLs
  • old category URLs
  • old post URLs
  • URLs with query strings
  • URLs with special characters
  • URLs from search results
  • URLs from old emails or documentation
  • URLs that start with built-in paths like /t, /c, or /u
  • external URL redirects

Troubleshooting

If an old URL does not redirect, check these common causes:

  1. Confirm the permalink exists.
  2. Confirm the permalink URL is the old path, not the full old domain.
  3. Check whether the old URL includes a query string.
  4. If using a normalization, confirm the normalized path matches a saved permalink.
  5. Check whether a built-in route is taking precedence.
  6. Check whether the destination is private or restricted.
  7. Check for encoding differences, such as %20 vs +.
  8. Check whether a normalization changed the URL when the permalink was saved.
  9. Confirm the content was migrated.
  10. Test with the exact old URL from a browser or with curl -I.

The most common issues are:

  • entering the full old URL instead of the old path
  • missing or unexpected query strings
  • expecting wildcards or regexes to work in the permalink URL field
  • old URLs overlapping with built-in paths
  • private or restricted destinations
  • encoding differences
  • normalizations changing the path unexpectedly

SEO notes

After a migration, search engines may report old URLs as redirected. This is expected if those old URLs now redirect to the correct new pages.

For best results:

  • redirect old URLs to relevant new pages
  • avoid unnecessary redirect chains
  • avoid redirecting many unrelated URLs to the homepage
  • keep redirects in place long enough for users and search engines to update
  • test important URLs from your old sitemap, analytics, or search console data

Last edited by @ruben 2026-06-17T23:25:56Z

Last checked by @ruben 2026-06-17T23:25:29Z

Check documentPerform check on document:
37 Likes