# 使用永久链接将旧论坛URL重定向到新的Discourse URL

**URL:** https://meta.discourse.org/t/redirect-old-forum-urls-to-new-discourse-urls-using-permalinks/20930
**Category:** Migrating to Discourse
**Tags:** how-to
**Created:** [2014年十月8日 19:03 UTC](https://meta.discourse.org/t/redirect-old-forum-urls-to-new-discourse-urls-using-permalinks/20930 "2014-10-08T19:03:52Z")
**Posts on this page:** 1
**Showing post:** 1

<div class="post-metadata">

### Author: ![Discourse](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/discourse/32/148734_2.png) [@Discourse](https://meta.discourse.org/u/Discourse)
#### Post date: [2014年十月8日 19:03 UTC](https://meta.discourse.org/t/redirect-old-forum-urls-to-new-discourse-urls-using-permalinks/20930/1 "2014-10-08T19:03:52Z")

</div>

> 🔖 本参考文档解释了 Discourse 永久链接如何将旧 URL 路径重定向到新目的地、永久链接规范化（normalizations）的工作原理，以及如何在迁移或 URL 结构更改后测试和排查重定向问题。
> 
> 🙋 所需用户级别：管理员
> 
> 💻 仅在使用导入器、脚本或 Rails 控制台创建永久链接时需要控制台访问权限

当你从其他论坛迁移或更改站点的 URL 结构时，来自搜索引擎、书签、电子邮件或其他网站的旧链接可能不再指向正确的页面。

永久链接允许你使用 `301 Moved Permanently`（永久移动）重定向将旧 URL 路径重定向到其新目的地。

永久链接是精确的路径映射。例如，如果以下旧 URL 不再有效：

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

```

你可以创建一个永久链接，使其重定向到新主题：

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

```

## 什么是永久链接

永久链接是从一个旧 URL 路径到一个新目的地的精确映射。

例如：

```yaml
forum/topic/123 → /t/welcome-to-our-community/456

```

在以下情况下，永久链接非常有用：

- 你从其他论坛平台迁移
- 旧链接已被搜索引擎索引
- 旧链接被用于电子邮件、文档或其他网站
- 你更改了站点 URL 的结构
- 你希望旧路径重定向到主题、帖子、分类、标签、用户或外部 URL

每个永久链接 URL 映射到一个目的地。

永久链接是精确匹配。永久链接 URL 字段 **不支持** 通配符或正则表达式。如果许多旧 URL 遵循可预测的模式，请使用永久链接规范化在精确查找之前转换传入的 URL。

## 永久链接与规范化

永久链接和规范化执行不同的操作：

| 功能 | 作用 | 示例 |
| --- | --- | --- |
| 永久链接 | 将一个保存的旧路径重定向到一个目的地 | `old/topic/123 → Topic 456` |
| 规范化 | 在永久链接查找之前更改传入的路径 | `old/topic/123-title → old/topic/123` |

规范化本身不会执行重定向。它仅更改用于查找已保存永久链接的路径。

规范化应用后，你仍然需要匹配的永久链接。

## 快速示例

### 重定向旧主题 URL

旧 URL：

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

```

永久链接 URL：

```yaml
forum/topic/123

```

目的地类型：

```yaml
Topic

```

目的地：

```yaml
456

```

结果：

```yaml
/forum/topic/123 → /t/welcome-to-our-community/456

```

### 重定向带有查询字符串的旧 URL

旧 URL：

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

```

永久链接 URL：

```yaml
viewtopic.php?t=123

```

目的地类型：

```yaml
Topic

```

目的地：

```yaml
456

```

结果：

```yaml
/viewtopic.php?t=123 → /t/welcome-to-our-community/456

```

### 重定向具有相同模式的许多旧 URL

旧 URL：

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

```

你可以使用规范化在查找之前简化它们：

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

```

然后创建精确的永久链接：

```yaml
forum/123 → Topic 1001
forum/456 → Topic 1002

```

## 作为永久链接 URL 输入什么

输入旧路径，而不是完整 URL。

例如，使用：

```yaml
forum/topic/123

```

或：

```yaml
/forum/topic/123

```

不要使用完整的旧 URL：

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

```

接受前导斜杠，但在保存永久链接时会将其移除。以下两个条目是等效的：

```yaml
/forum/topic/123
forum/topic/123

```

两者都存储为：

```yaml
forum/topic/123

```

保存永久链接时会移除前导和尾随空格。

## 查询字符串是匹配的一部分

永久链接匹配完整的请求路径，包括查询字符串。

这意味着以下两个 URL 是不同的永久链接匹配项：

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

```

如果旧 URL 包含查询字符串，则：

1. 创建一个包含查询字符串的永久链接，或
2. 使用永久链接规范化在匹配之前移除或简化查询字符串

对于大多数分析或跟踪参数，使用规范化通常比创建许多单独的永久链接更好。

例如，以下保存的永久链接：

```yaml
docs/123

```

不一定匹配以下请求的 URL：

```yaml
/docs/123?utm_source=newsletter

```

除非规范化在查找之前移除了查询字符串。

从分析工具、搜索控制台报告、浏览器或电子邮件活动中复制旧 URL 时，请检查是否添加了跟踪参数。

## URL 片段不是匹配的一部分

URL 片段不会发送到服务器。

例如，以下 URL：

```yaml
/old/topic#post-12

```

到达服务器时显示为：

```yaml
/old/topic

```

永久链接或规范化无法匹配：

```yaml
#post-12

```

除非 `#` 在实际请求中被编码为 `%23`。

## 支持的永久链接目的地

永久链接可以重定向到以下目的地类型之一：

- 主题 (Topic)
- 帖子 (Post)
- 分类 (Category)
- 标签 (Tag)
- 用户 (User)
- 外部或相对 URL

外部或相对 URL 目的地可用于此类重定向：

```yaml
old/privacy-policy → https://archive.discourse.example.com/privacy

```

或：

```yaml
old/preferences → /my/preferences

```

谨慎使用外部 URL 目的地。它们会将访问者重定向到你的站点之外，并且不检查目标 URL 是否存在。

重定向到迁移后的主题、帖子、分类、标签或用户时，请优先使用内部目的地类型。当目标页面不由内部对象表示时，使用外部 URL 目的地。

## 手动添加永久链接

要手动添加永久链接：

1. 前往 `/admin/config/permalinks`。
2. 选择 **永久链接 (Permalinks)** 选项卡。
3. 点击 **添加永久链接 (Add permalink)**。
4. 在 **URL** 字段中输入旧路径。
5. 选择 **永久链接类型 (Permalink type)**。
6. 输入或选择目的地。
7. 保存永久链接。
8. 在浏览器中使用 `curl` 测试旧 URL。

示例：

```yaml
URL:
forum/topic/123

永久链接类型：
Topic

目的地：
456

```

对以下 URL 的请求：

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

```

将重定向到主题 `456`。

## 目的地值

目的地值取决于永久链接类型。

| 永久链接类型 | 目的地 |
| --- | --- |
| 主题 (Topic) | 一个主题 |
| 帖子 (Post) | 一个帖子 |
| 分类 (Category) | 一个分类 |
| 标签 (Tag) | 一个标签 |
| 用户 (User) | 一个用户 |
| 外部或相对 URL | 完整的外部 URL 或相对路径 |

示例：

```plaintext
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

```

## 在迁移期间创建永久链接

许多官方导入器会自动为迁移后的分类、主题、帖子或用户创建永久链接。

对于大型迁移，永久链接通常由导入器或迁移脚本创建，而不是逐个手动输入。

如果你正在编写自定义导入器，请为迁移后应继续工作的旧 URL 创建永久链接记录。

例如：

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

```

在此示例中，`discussion/12345` 是旧路径，`987` 是 Discourse 主题 ID。

也接受前导斜杠：

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

```

两个示例都将永久链接 URL 存储为：

```yaml
discussion/12345

```

要重定向到外部 URL：

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

```

在导入器中创建永久链接时：

- 存储旧路径，而不是完整的旧域名
- 仅当匹配需要时才包含查询字符串
- 确保每个旧路径映射到一个目的地
- 在插入记录之前检查重复项
- 记住永久链接 URL 必须是唯一的

如果你运行多次测试迁移，最终的主题和帖子 ID 在每次运行之间可能会发生变化。在生产迁移数据稳定时生成或验证最终的永久链接映射。

## 什么是永久链接规范化

永久链接规范化在查找匹配的永久链接之前更改传入的旧路径。

当许多旧 URL 遵循相同的可预测模式时，规范化非常有用。

例如，假设你的旧论坛使用如下 URL：

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

```

但你保存的永久链接更简单：

```yaml
forum/123

```

规范化可以将传入的请求转换为保存的永久链接路径：

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

```

然后永久链接可以重定向：

```yaml
forum/123 → /t/how-do-i-reset-my-password/456

```

规范化本身不会执行重定向。它仅更改用于永久链接查找的路径。

你仍然需要规范化路径的匹配永久链接。

大多数站点不需要永久链接规范化。仅当许多旧 URL 遵循可预测的模式，且为每个旧 URL 创建一个永久链接不切实际时才使用它们。

## 永久链接和规范化如何协同工作

当请求旧 URL 时，流程如下：

```yaml
访客请求旧 URL
        ↓
应用永久链接规范化（如果已配置）
        ↓
使用 resulting 路径执行永久链接查找
        ↓
如果存在匹配的永久链接，访客将被重定向

```

例如：

```yaml
请求的 URL：
/old/topic/123?utm_source=newsletter

规范化将其更改为：
old/topic/123

永久链接将其重定向到：
/t/new-topic-title/456

```

## 添加永久链接规范化

永久链接规范化是从永久链接设置中配置的。

要添加一个：

1. 前往 `/admin/config/permalinks`。
2. 选择 **设置 (Settings)** 选项卡。
3. 将规则添加到 `permalink normalizations` 设置。
4. 保存设置。
5. 测试应被规范化的旧 URL。
6. 确认规范化后的路径匹配现有的永久链接。

语法为：

```yaml
/<regex>/<replacement>

```

多个规则用 `|` 分隔。

例如：

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

```

这可以将：

```yaml
old/topic/123-my-old-title

```

转换为：

```yaml
topic/123

```

替换部分使用 Ruby 替换语法，因此捕获组写为：

```yaml
\1
\2
\3

```

## 重要的规范化规则

保持规范化规则简单。

- 规范化使用 Ruby 正则表达式。
- 正则表达式部分中的字面 `/` 字符必须转义为 `\/`。
- 替换部分中的字面 `/` 字符不需要转义。
- 多个规范化规则用 `|` 分隔。
- 因为 `|` 用于分隔规则，所以避免使用带有 `|` 的正则表达式交替。
- 每个规则使用单个替换，而不是全局替换。
- 规则按顺序应用。
- 匹配的规则按顺序应用。
- 规范化在永久链接查找之前应用。
- 规范化在保存永久链接记录时也应用。
- 设置验证器会捕获无效的正则表达式，但可能无法捕获每个行为与预期不同的规则。

> ⚠ **警告：** 在保存永久链接记录时会应用规范化。如果在管理界面中输入未规范化的 URL，它可能会被存储为规范化后的结果。如果在创建永久链接后添加或更改规范化，请仔细测试并检查新保存的永久链接 URL 是如何存储的。

## 规范化示例

### 重定向带有额外标题文本的旧 URL

旧 URL：

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

```

你可以将它们规范化为更简单的永久链接路径：

```yaml
forum/123
forum/456

```

示例规范化：

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

```

然后创建永久链接：

```yaml
forum/123 → Topic 1001
forum/456 → Topic 1002

```

### 忽略跟踪查询字符串

旧请求 URL：

```yaml
/docs/123?utm_source=newsletter

```

保存的永久链接：

```yaml
docs/123

```

规范化：

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

```

这将在永久链接查找之前移除查询字符串。

### 保留重要的查询参数

一些旧论坛使用查询参数作为稳定的主题标识符。

例如：

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

```

你可能希望将它们规范化为：

```yaml
viewtopic.php?t=123

```

示例规范化：

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

```

然后创建永久链接：

```yaml
viewtopic.php?t=123 → Topic 456

```

## 正则表达式和通配符重定向

永久链接 URL 字段不支持正则表达式或通配符。

以下作为永久链接 URL 将不起作用：

```yaml
forum/topic/*

```

以下作为永久链接 URL 也不起作用：

```yaml
forum/topic/(\d+)

```

如果你需要基于模式的处理，请使用永久链接规范化将传入的 URL 重写为可以匹配精确保存的永久链接的形式。

例如：

```yaml
/forum/topic/123-title → forum/topic/123

```

然后创建正常的精确永久链接：

```yaml
forum/topic/123 → Topic 456

```

## 路由优先级和内置路径

永久链接路由在常规应用路由之后进行检查。

这意味着有效的现有路由可能在检查永久链接之前正常解析。

小心以内置路径开头的旧 URL，例如：

```yaml
/t
/c
/u
/tag
/tags

```

例如，以下旧论坛 URL 可能看起来像内置的分类路由：

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

```

如果旧 URL 与有效的现有路由冲突，有效路由可能优先于永久链接。

同样，如果你为看起来像现有主题路由的路径创建永久链接，例如：

```yaml
t/123

```

常规主题路由可能在永久链接查找之前被处理。

一些内置的未找到路由具有额外的回退行为，但你不应该依赖于此。始终测试与内置路径重叠的旧 URL。

如果你需要旧主题 URL 重定向到其他地方，旧主题通常必须不再解析为有效的主题路由。

## 权限和私有内容

指向内部目的地的永久链接遵循正常的权限设置。

如果永久链接指向私有或受限的主题、帖子、分类、标签或用户，无法访问该目的地的访客将获得 `404` 错误，而不是重定向。

指向外部 URL 的永久链接不检查内部内容权限。

## 特殊字符和编码 URL

仔细测试带有特殊字符的 URL。

这包括带有以下内容的 URL：

- 空格
- `+`
- `%`
- `&`
- `'`
- `:`
- 括号
- 非拉丁字符

永久链接匹配使用移除前导斜杠并应用配置的规范化后的编码请求路径。编码差异可能导致 URL 无法匹配你期望的永久链接。

例如，以下 URL 可能不等效，具体取决于旧 URL 的请求和存储方式：

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

```

在 URL 示例中使用 `%20` 表示空格。在 URL 路径中，`+` 不等于空格。

字符如 `&`、`?` 和 `#` 在 URL 中具有特殊含义。如果它们应作为字面路径字符，则应进行百分号编码。

如有疑问，请测试用户或搜索引擎将请求的确切旧 URL。

## 限制

永久链接旨在将旧路径重定向到新目的地。它们不是通用重定向或重写引擎。

重要限制：

- 永久链接 URL 存储为路径和查询字符串，而不是完整 URL。
- 完整 URL 不会自动转换为路径。
- 规范化后，永久链接 URL 必须是唯一的。
- 永久链接 URL 只能有一个目的地。
- 支持的目的地仅限于主题、帖子、分类、标签、用户以及外部或相对 URL。
- 永久链接使用 `301 Moved Permanently`；没有每个永久链接使用 `302` 的选项。
- 永久链接 URL 字段不支持通配符或正则表达式。
- 查询字符串是查找键的一部分。
- URL 片段（如 `#post-12`）不会发送到服务器，也无法匹配。
- 匹配不使用请求方案或主机。
- 原生路由在捕获所有永久链接路由之前进行检查。
- 内部目的地会进行权限检查。
- 外部 URL 目的地绕过内部内容权限检查。
- 永久链接规范化使用 Ruby 正则表达式。
- 规范化规则用 `|` 分隔，因此避免将 `|` 用作正则表达式交替。
- 规范化规则每个规则使用单个替换，而不是全局替换。
- 规范化在查找之前和保存永久链接记录之前均应用。
- 设置验证器会捕获无效的正则表达式，但可能无法捕获每个行为与预期不同的规则。

对于大多数站点，简单的单对单永久链接比复杂的规范化规则更容易维护。仅当旧 URL 遵循可预测的模式时才使用规范化。

## 避免将所有旧 URL 重定向到主页

不要将所有旧 URL 重定向到你的主页。

将每个旧 URL 重定向到最相关的新页面。如果没有等效内容，`404` 可能比将用户和搜索引擎发送到不相关的页面更好。

良好的重定向是具体的：

```yaml
old/topic/123 → 关于同一主题的新主题

```

糟糕的重定向是通用的：

```yaml
old/topic/123 → 主页
old/topic/456 → 主页
old/topic/789 → 主页

```

## 测试你的重定向

创建永久链接或规范化后，测试一些旧 URL 的样本。

你可以在浏览器中测试，或使用：

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

```

工作的永久链接重定向应返回类似以下的永久重定向：

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

```

测试每个旧 URL 模式的示例，包括：

- 旧主题 URL
- 旧分类 URL
- 旧帖子 URL
- 带有查询字符串的 URL
- 带有特殊字符的 URL
- 来自搜索结果的 URL
- 来自旧电子邮件或文档的 URL
- 以 `/t`、`/c` 或 `/u` 等内置路径开头的 URL
- 外部 URL 重定向

## 故障排除

如果旧 URL 没有重定向，请检查以下常见原因：

1. 确认永久链接存在。
2. 确认永久链接 URL 是旧路径，而不是完整的旧域名。
3. 检查旧 URL 是否包含查询字符串。
4. 如果使用规范化，确认规范化后的路径匹配保存的永久链接。
5. 检查是否有内置路由优先。
6. 检查目的地是否私有或受限。
7. 检查编码差异，例如 `%20` 与 `+`。
8. 检查永久链接保存时规范化是否更改了 URL。
9. 确认内容已迁移。
10. 使用浏览器或 `curl -I` 测试确切的旧 URL。

最常见的问题是：

- 输入了完整的旧 URL 而不是旧路径
- 缺少或意外的查询字符串
- 期望通配符或正则表达式在永久链接 URL 字段中起作用
- 旧 URL 与内置路径重叠
- 私有或受限的目的地
- 编码差异
- 规范化意外更改了路径

---

_[View the full topic](https://meta.discourse.org/t/redirect-old-forum-urls-to-new-discourse-urls-using-permalinks/20930)._
