URL 管理

在 Hugo 中,URL 管理是一个灵活且强大的方面,它允许您通过前置元数据(front matter)和站点配置(site configuration)来控制网站链接的结构和外观。

默认 URL 行为

基本规则

默认情况下,当 Hugo 渲染页面时,生成的 URL 会匹配内容目录中的文件路径。

例如:

  • 文件路径:content/posts/my-first-post.md
  • 默认 URL:/posts/my-first-post/

文件系统映射

content/
├── _index.md           → /
├── about.md            → /about/
├── contact.md          → /contact/
├── posts/
│   ├── _index.md       → /posts/
│   ├── post-1.md       → /posts/post-1/
│   └── post-2.md       → /posts/post-2/
└── docs/
    ├── _index.md       → /docs/
    └── getting-started.md → /docs/getting-started/

前置元数据 URL 控制

前置元数据中的 slugurl 字段对 URL 管理最为重要。

slug 字段

slug 用于覆盖 URL 路径的最后一个片段

---
title: "My First Post"
slug: "my-first-post"
---

使用示例

# content/posts/article-with-long-name.md
---
title: "一篇关于 Hugo 静态网站生成器的详细介绍文章"
slug: "hugo-intro"
---

结果:

  • 默认 URL:/posts/article-with-long-name/
  • 使用 slug:/posts/hugo-intro/

url 字段

url 用于覆盖整个 URL 路径

---
title: "About Us"
url: "/company/about/"
---

重要特性

  • 适用于常规页面和章节页面
  • Hugo 不会净化 url 字段,需要小心使用
  • url 优先于 slug

使用示例

# content/posts/special-announcement.md
---
title: "重要公告"
url: "/announcement/2025/important-news/"
---

# content/docs/api-reference.md
---
title: "API 参考"
url: "/developers/api/"
---

多语言 URL 处理

在多语言站点中,url 值的处理方式:

# content/zh/about.md
---
title: "关于我们"
url: "/guanyu/"  # 不带前导斜杠:相对于 baseURL + 语言前缀
---

# content/en/about.md  
---
title: "About Us"
url: "/about/"   # 带前导斜杠:相对于 baseURL
---

站点配置 URL 管理

在站点配置中为不同页面类型定义 URL 模式:

# hugo.toml
[permalinks]
  posts = "/blog/:year/:month/:title/"
  docs = "/documentation/:section/:title/"
  news = "/news/:year/:slug/"

可用占位符

  • :year:年份(2025)
  • :month:月份(01-12)
  • :monthname:月份名称(January)
  • :day:日期(01-31)
  • :weekday:星期(Monday)
  • :weekdayname:星期名称(Monday)
  • :yearday:年中的天数(001-366)
  • :section:内容章节
  • :sections:所有章节的层次结构
  • :title:页面标题(URL 化)
  • :slug:页面 slug
  • :filename:文件名(不含扩展名)

使用示例

[permalinks]
  # 博客文章:/blog/2025/01/my-post-title/
  posts = "/blog/:year/:month/:title/"
  
  # 文档:/docs/getting-started/installation/
  docs = "/docs/:section/:title/"
  
  # 新闻:/news/2025/important-announcement/
  news = "/news/:year/:slug/"
  
  # 项目:/projects/my-awesome-project/
  projects = "/projects/:slug/"

语言特定永久链接

[languages.zh]
  [languages.zh.permalinks]
    posts = "/wenzhang/:year/:month/:title/"
    docs = "/wendang/:section/:title/"
    
[languages.en]
  [languages.en.permalinks]
    posts = "/posts/:year/:month/:title/"
    docs = "/docs/:section/:title/"

uglyURLs 配置

控制 URL 的美观程度:

# hugo.toml
uglyURLs = false  # 默认:漂亮 URL (/posts/my-post/)
# uglyURLs = true   # 丑陋 URL (/posts/my-post.html)

针对特定章节配置

[uglyURLs]
  posts = true      # /posts/my-post.html
  docs = false      # /docs/guide/

baseURL 配置

站点的绝对 URL 基础:

baseURL = 'https://example.com/'

# 多环境配置
[environments.production]
  baseURL = 'https://example.com/'
  
[environments.staging]
  baseURL = 'https://staging.example.com/'
  
[environments.development]
  baseURL = 'http://localhost:1313/'

别名和重定向

aliases 字段

通过 aliases 创建旧 URL 的重定向:

---
title: "新页面标题"
url: "/new-path/"
aliases:
  - "/old-path/"
  - "/another-old-path/"
  - "/legacy/old-page.html"
---

自动生成重定向

Hugo 会为每个别名生成客户端重定向文件:

<!-- public/old-path/index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>Redirecting...</title>
  <link rel="canonical" href="/new-path/">
  <meta name="robots" content="noindex">
  <meta charset="utf-8">
  <meta http-equiv="refresh" content="0; url=/new-path/">
</head>
<body>
  <h1>Redirecting...</h1>
  <a href="/new-path/">Click here if you are not redirected</a>
</body>
</html>

批量重定向

# content/posts/renamed-article.md
---
title: "重命名的文章"
date: 2025-06-19
aliases:
  - "/blog/old-article-name/"
  - "/posts/2024/12/old-article-name/"
  - "/articles/old-article-name.html"
---

高级 URL 管理

条件 URL 生成

# 使用占位符的动态 URL
---
title: "{{ .Title }}"
url: "/{{ .Section }}/{{ .Date.Format \"2006\" }}/{{ .Params.category | urlize }}/"
---

自定义 URL 模式

# hugo.toml
[permalinks]
  # 复杂的 URL 模式
  posts = "/:sections[1:]/:year/:month/:day/:slug/"
  
  # 条件模式(通过 cascade 实现)
  tutorials = "/learn/:title/"
  guides = "/guides/:section/:title/"

动态重定向配置

# hugo.toml
[mediaTypes]
  [mediaTypes."text/netlify"]
    suffixes = [""]

[outputFormats]
  [outputFormats.redirects]
    mediaType = "text/netlify"
    baseName = "_redirects"
    isPlainText = true
    notAlternative = true

[outputs]
  home = ["HTML", "redirects"]
<!-- layouts/index.redirects -->
{{ range .Site.AllPages -}}
  {{ range .Aliases -}}
    {{ . }} {{ $.Permalink }} 301
  {{ end -}}
{{ end -}}

模板中的 URL 处理

基本 URL 方法

<!-- 绝对 URL -->
{{ .Permalink }}
<!-- https://example.com/posts/my-post/ -->

<!-- 相对 URL -->
{{ .RelPermalink }}
<!-- /posts/my-post/ -->

<!-- 基础 URL -->
{{ .Site.BaseURL }}
<!-- https://example.com/ -->

URL 构建函数

<!-- 构建相对 URL -->
{{ relURL "/css/style.css" }}
<!-- /css/style.css -->

<!-- 构建绝对 URL -->
{{ absURL "/css/style.css" }}
<!-- https://example.com/css/style.css -->

<!-- URL 参数编码 -->
{{ urlquery "hello world" }}
<!-- hello+world -->

引用其他页面

<!-- 使用 ref 短代码 -->
{{ ref . "posts/my-other-post.md" }}

<!-- 使用 relref 短代码 -->
{{ relref . "docs/getting-started.md" }}

<!-- 在模板中使用 -->
{{ with .Site.GetPage "/posts/featured-post" }}
  <a href="{{ .Permalink }}">{{ .Title }}</a>
{{ end }}

条件链接生成

<!-- 检查页面存在性 -->
{{ $page := .Site.GetPage "/about" }}
{{ if $page }}
  <a href="{{ $page.Permalink }}">{{ $page.Title }}</a>
{{ else }}
  <span>About page not found</span>
{{ end }}

<!-- 多语言链接 -->
{{ range .Site.Languages }}
  {{ if eq . $.Site.Language }}
    <span class="current">{{ .LanguageName }}</span>
  {{ else }}
    {{ with $.Translations.Get .Lang }}
      <a href="{{ .Permalink }}">{{ .Language.LanguageName }}</a>
    {{ end }}
  {{ end }}
{{ end }}

SEO 优化

规范链接

<!-- 设置规范 URL -->
<link rel="canonical" href="{{ .Permalink }}">

<!-- 多语言规范链接 -->
{{ range .Translations }}
  <link rel="alternate" hreflang="{{ .Lang }}" href="{{ .Permalink }}">
{{ end }}
<link rel="alternate" hreflang="x-default" href="{{ .Site.BaseURL }}">

Open Graph URL

<meta property="og:url" content="{{ .Permalink }}">
<meta property="og:type" content="article">
<meta property="og:title" content="{{ .Title }}">

结构化数据

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "url": "{{ .Permalink }}",
  "headline": "{{ .Title }}",
  "datePublished": "{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}",
  "author": {
    "@type": "Person",
    "name": "{{ .Params.author }}"
  }
}
</script>

调试和验证

检查 URL 生成

# 显示所有页面的 URL
hugo list all | grep -E "(permalink|path)"

# 检查特定页面的 URL
hugo list all | grep "my-post"

验证重定向

# 检查生成的重定向文件
find public -name "index.html" -path "*/old-path/*"

# 测试重定向
curl -I http://localhost:1313/old-path/

URL 调试模板

<!-- 在开发环境显示 URL 调试信息 -->
{{ if not hugo.IsProduction }}
  <div style="background: #f0f0f0; padding: 10px; margin: 10px 0;">
    <h4>URL Debug Info</h4>
    <p><strong>Permalink:</strong> {{ .Permalink }}</p>
    <p><strong>RelPermalink:</strong> {{ .RelPermalink }}</p>
    <p><strong>Kind:</strong> {{ .Kind }}</p>
    <p><strong>Section:</strong> {{ .Section }}</p>
    <p><strong>Type:</strong> {{ .Type }}</p>
    {{ with .Aliases }}
      <p><strong>Aliases:</strong> {{ delimit . ", " }}</p>
    {{ end }}
  </div>
{{ end }}

最佳实践

1. URL 设计原则

  • 保持简洁:避免过长的 URL
  • 语义化:URL 应该反映内容结构
  • 一致性:在整个站点保持 URL 模式一致
  • 永久性:避免频繁更改 URL 结构

2. 重定向管理

# 规范的重定向配置
---
title: "新文章标题"
date: 2025-06-19
url: "/posts/new-article-title/"
aliases:
  - "/blog/old-article-title/"  # 旧博客路径
  - "/2024/12/old-article/"     # 旧日期路径
  - "/articles/old-title.html"  # 遗留 HTML 路径
---

3. 多语言 URL 策略

# 建议的多语言配置
[languages.zh]
  [languages.zh.permalinks]
    posts = "/wenzhang/:year/:month/:title/"
    
[languages.en]  
  [languages.en.permalinks]
    posts = "/posts/:year/:month/:title/"

4. 性能考虑

  • 避免过深的 URL 层次结构
  • 使用静态 URL 而非动态生成
  • 合理使用别名,避免过多重定向

5. 安全注意事项

# 避免特殊字符和保留字符
---
title: "安全的文章标题"
slug: "safe-article-title"  # 而不是包含特殊字符的 slug
url: "/posts/safe-path/"    # 避免 <、>、: 等字符
---

通过灵活的 URL 管理,您可以创建用户友好、SEO 优化且易于维护的网站链接结构。

文章导航

章节内容

这是章节的内容页面。

章节概览