2019-10-21 04:22:28 -04:00
---
2023-07-29 05:15:54 -04:00
title: URL management
2023-05-22 10:43:12 -04:00
description: Control the structure and appearance of URLs through front matter entries and settings in your site configuration.
2019-10-21 04:22:28 -04:00
categories: [content management]
2022-11-17 10:14:29 -05:00
keywords: [aliases,redirects,permalinks,urls]
2019-10-21 04:22:28 -04:00
menu:
docs:
2022-11-17 10:14:29 -05:00
parent: content-management
weight: 180
2019-10-21 04:22:28 -04:00
toc: true
2022-11-17 10:14:29 -05:00
weight: 180
aliases: [/extras/permalinks/,/extras/aliases/,/extras/urls/,/doc/redirects/,/doc/alias/,/doc/aliases/]
2019-10-21 04:22:28 -04:00
---
2023-05-22 10:43:12 -04:00
## Overview
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
By default, when Hugo renders a page, the resulting URL matches the file path within the `content` directory. For example:
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
```text
content/posts/post-1.md → https://example.org/posts/post-1/
```
You can change the structure and appearance of URLs with front matter values and site configuration options.
## Front matter
### `slug`
Set the `slug` in front matter to override the last segment of the path. The `slug` value does not affect section pages.
{{< code-toggle file = "content/posts/post-1.md" copy = false fm = true > }}
title = 'My First Post'
slug = 'my-first-post'
{{< / code-toggle > }}
The resulting URL will be:
```text
https://example.org/posts/my-first-post/
```
### `url`
Set the `url` in front matter to override the entire path. Use this with either regular pages or section pages.
With this front matter:
{{< code-toggle file = "content/posts/post-1.md" copy = false fm = true > }}
title = 'My First Article'
url = '/articles/my-first-article'
{{< / code-toggle > }}
The resulting URL will be:
```text
https://example.org/articles/my-first-article/
```
If you include a file extension:
{{< code-toggle file = "content/posts/post-1.md" copy = false fm = true > }}
title = 'My First Article'
url = '/articles/my-first-article.html'
{{< / code-toggle > }}
The resulting URL will be:
```text
https://example.org/articles/my-first-article.html
```
In a monolingual site, a `url` value with or without a leading slash is relative to the `baseURL` .
In a multilingual site:
- A `url` value with a leading slash is relative to the `baseURL` .
- A `url` value without a leading slash is relative to the `baseURL` plus the language prefix.
Site type|Front matter `url` |Resulting URL
:--|:--|:--
monolingual|`/about`|`https://example.org/about/`
monolingual|`about`|`https://example.org/about/`
multilingual|`/about`|`https://example.org/about/`
multilingual|`about`|`https://example.org/de/about/`
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
If you set both `slug` and `url` in front matter, the `url` value takes precedence.
## Site configuration
### Permalinks
2023-07-29 05:15:54 -04:00
In your site configuration, define a URL pattern for each top-level section. Each URL pattern can target a given language and/or [page kind].
2023-05-22 10:43:12 -04:00
2023-07-29 05:15:54 -04:00
Front matter `url` values override the URL patterns defined in the `permalinks` section of your site configuration.
2019-10-21 04:22:28 -04:00
2023-07-29 05:15:54 -04:00
[page kind]: /templates/section-templates/#page-kinds
#### Monolingual examples {#permalinks-monolingual-examples}
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
With this content structure:
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
```text
content/
├── posts/
2023-07-29 05:15:54 -04:00
│ ├── bash-in-slow-motion.md
│ └── tls-in-a-nutshell.md
├── tutorials/
│ ├── git-for-beginners.md
│ └── javascript-bundling-with-hugo.md
2023-05-22 10:43:12 -04:00
└── _index.md
```
2023-07-29 05:15:54 -04:00
Render tutorials under "training", and render the posts under "articles" with a date-base hierarchy:
2023-05-22 10:43:12 -04:00
2023-05-27 10:59:59 -04:00
{{< code-toggle file = "hugo" copy = false > }}
2023-07-29 05:15:54 -04:00
[permalinks.page]
posts = '/articles/:year/:month/:slug/'
tutorials = '/training/:slug/'
[permalinks.section]
posts = '/articles/'
tutorials = '/training/'
2019-10-21 04:22:28 -04:00
{{< / code-toggle > }}
2023-05-22 10:43:12 -04:00
The structure of the published site will be:
```text
public/
2023-07-29 05:15:54 -04:00
├── articles/
│ ├── 2023/
│ │ ├── 04/
│ │ │ └── bash-in-slow-motion/
│ │ │ └── index.html
│ │ └── 06/
│ │ └── tls-in-a-nutshell/
│ │ └── index.html
│ └── index.html
├── training/
│ ├── git-for-beginners/
│ │ └── index.html
│ ├── javascript-bundling-with-hugo/
│ │ └── index.html
│ └── index.html
2023-05-22 10:43:12 -04:00
└── index.html
```
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
To create a date-based hierarchy for regular pages in the content root:
2020-05-31 06:43:23 -04:00
2023-05-27 10:59:59 -04:00
{{< code-toggle file = "hugo" copy = false > }}
2023-07-29 05:15:54 -04:00
[permalinks.page]
"/" = "/:year/:month/:slug/"
2020-05-31 06:43:23 -04:00
{{< / code-toggle > }}
2023-07-29 05:15:54 -04:00
Use the same approach with taxonomy terms. For example, to omit the taxonomy segment of the URL:
{{< code-toggle file = "hugo" copy = false > }}
[permalinks.term]
'tags' = '/:slug/'
{{< / code-toggle > }}
#### Multilingual example {#permalinks-multilingual-example}
2019-12-15 04:35:09 -05:00
2023-07-29 05:15:54 -04:00
Use the `permalinks` configuration as a component of your localization strategy.
With this content structure:
```text
content/
├── de/
│ ├── books/
│ │ ├── les-miserables.md
│ │ └── the-hunchback-of-notre-dame.md
│ └── _index.md
└── en/
├── books/
│ ├── les-miserables.md
│ └── the-hunchback-of-notre-dame.md
└── _index.md
```
And this site configuration:
2023-05-22 10:43:12 -04:00
2023-05-27 10:59:59 -04:00
{{< code-toggle file = "hugo" copy = false > }}
2023-07-29 05:15:54 -04:00
defaultContentLanguage = 'en'
defaultContentLanguageInSubdir = true
[languages.en]
contentDir = 'content/en'
languageCode = 'en-US'
languageDirection = 'ltr'
languageName = 'English'
weight = 1
[languages.en.permalinks.page]
books = "/books/:slug/"
[languages.en.permalinks.section]
books = "/books/"
[languages.es]
contentDir = 'content/de'
languageCode = 'es-ES'
languageDirection = 'ltr'
languageName = 'Español'
weight = 2
[languages.es.permalinks.page]
books = "/libros/:slug/"
[languages.es.permalinks.section]
books = "/libros/"
2019-12-15 04:35:09 -05:00
{{< / code-toggle > }}
2023-07-29 05:15:54 -04:00
The structure of the published site will be:
```text
public/
├── en/
│ ├── books/
│ │ ├── les-miserables/
│ │ │ └── index.html
│ │ ├── the-hunchback-of-notre-dame/
│ │ │ └── index.html
│ │ └── index.html
│ └── index.html
├── es/
│ ├── libros/
│ │ ├── les-miserables/
│ │ │ └── index.html
│ │ ├── the-hunchback-of-notre-dame/
│ │ │ └── index.html
│ │ └── index.html
│ └── index.html
└── index.html
````
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
#### Tokens
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
Use these tokens when defining the URL pattern. The `date` field in front matter determines the value of time-related tokens.
2019-10-21 04:22:28 -04:00
`:year`
: the 4-digit year
`:month`
: the 2-digit month
`:monthname`
: the name of the month
`:day`
: the 2-digit day
`:weekday`
: the 1-digit day of the week (Sunday = 0)
`:weekdayname`
: the name of the day of the week
`:yearday`
: the 1- to 3-digit day of the year
`:section`
: the content's section
`:sections`
2022-12-02 03:19:23 -05:00
: the content's sections hierarchy. You can use a selection of the sections using _slice syntax_ : `:sections[1:]` includes all but the first, `:sections[:last]` includes all but the last, `:sections[last]` includes only the last, `:sections[1:2]` includes section 2 and 3. Note that this slice access will not throw any out-of-bounds errors, so you don't have to be exact.
2019-10-21 04:22:28 -04:00
`:title`
: the content's title
`:slug`
: the content's slug (or title if no slug is provided in the front matter)
2022-06-28 14:51:33 -04:00
`:slugorfilename`
2023-07-29 05:15:54 -04:00
: the content's slug (or file name if no slug is provided in the front matter)
2022-06-28 14:51:33 -04:00
2019-10-21 04:22:28 -04:00
`:filename`
2023-07-29 05:15:54 -04:00
: the content's file name (without extension)
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
For time-related values, you can also use the layout string components defined in Go's [time package]. For example:
2019-12-15 04:35:09 -05:00
2023-05-22 10:43:12 -04:00
[time package]: https://pkg.go.dev/time#pkg-constants
2019-10-21 04:22:28 -04:00
2023-05-27 10:59:59 -04:00
{{< code-toggle file = "hugo" copy = false > }}
2023-05-22 10:43:12 -04:00
permalinks:
posts: /:06/:1/:2/:title/
{{< / code-toggle > }}
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
### Appearance
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
The appearance of a URL is either ugly or pretty.
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
Type|Path|URL
:--|:--|:--
ugly|content/about.md|`https://example.org/about.html`
pretty|content/about.md|`https://example.org/about/`
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
By default, Hugo produces pretty URLs. To generate ugly URLs, change your site configuration:
2019-10-21 04:22:28 -04:00
2023-05-27 10:59:59 -04:00
{{< code-toggle file = "hugo" copy = false > }}
2023-05-22 10:43:12 -04:00
uglyURLs = true
{{< / code-toggle > }}
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
### Post-processing
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
Hugo provides two mutually exclusive configuration options to alter URLs _after_ it renders a page.
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
#### Canonical URLs
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
{{% note %}}
This is a legacy configuration option, superseded by template functions and markdown render hooks, and will likely be [removed in a future release].
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
[removed in a future release]: https://github.com/gohugoio/hugo/issues/4733
{{% /note %}}
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
If enabled, Hugo performs a search and replace _after_ it renders the page. It searches for site-relative URLs (those with a leading slash) associated with `action` , `href` , `src` , `srcset` , and `url` attributes. It then prepends the `baseURL` to create absolute URLs.
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
```text
< a href = "/about" > → < a href = "https://example.org/about/" >
< img src = "/a.gif" > → < img src = "https://example.org/a.gif" >
2019-10-21 04:22:28 -04:00
```
2023-05-22 10:43:12 -04:00
This is an imperfect, brute force approach that can affect content as well as HTML attributes. As noted above, this is a legacy configuration option that will likely be removed in a future release.
2022-03-26 05:04:57 -04:00
2023-05-22 10:43:12 -04:00
To enable:
2019-10-21 04:22:28 -04:00
2023-05-27 10:59:59 -04:00
{{< code-toggle file = "hugo" copy = false > }}
2023-05-22 10:43:12 -04:00
canonifyURLs = true
{{< / code-toggle > }}
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
#### Relative URLs
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
{{% note %}}
Do not enable this option unless you are creating a serverless site, navigable via the file system.
{{% /note %}}
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
If enabled, Hugo performs a search and replace _after_ it renders the page. It searches for site-relative URLs (those with a leading slash) associated with `action` , `href` , `src` , `srcset` , and `url` attributes. It then transforms the URL to be relative to the current page.
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
For example, when rendering `content/posts/post-1` :
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
```text
< a href = "/about" > → < a href = "../../about" >
< img src = "/a.gif" > → < img src = "../../a.gif" >
2019-10-21 04:22:28 -04:00
```
2023-05-22 10:43:12 -04:00
This is an imperfect, brute force approach that can affect content as well as HTML attributes. As noted above, do not enable this option unless you are creating a serverless site.
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
To enable:
2019-10-21 04:22:28 -04:00
2023-05-27 10:59:59 -04:00
{{< code-toggle file = "hugo" copy = false > }}
2023-05-22 10:43:12 -04:00
relativeURLs = true
{{< / code-toggle > }}
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
## Aliases
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
Create redirects from old URLs to new URLs with aliases:
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
- An alias with a leading slash is relative to the `baseURL`
- An alias without a leading slash is relative to the current directory
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
### Examples {#alias-examples}
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
Change the file name of an existing page, and create an alias from the previous URL to the new URL:
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
{{< code-toggle file = "content/posts/new-file-name.md" copy = false > }}
aliases = ['/posts/previous-file-name']
{{< / code-toggle > }}
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
Each of these directory-relative aliases is equivalent to the site-relative alias above:
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
- `previous-file-name`
- `./previous-file-name`
- `../posts/previous-file-name`
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
You can create more than one alias to the current page:
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
{{< code-toggle file = "content/posts/new-file-name.md" copy = false > }}
aliases = ['previous-file-name','original-file-name']
{{< / code-toggle > }}
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
In a multilingual site, use a directory-relative alias, or include the language prefix with a site-relative alias:
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
{{< code-toggle file = "content/posts/new-file-name.de.md" copy = false > }}
aliases = ['/de/posts/previous-file-name']
{{< / code-toggle > }}
2019-10-21 04:22:28 -04:00
2023-07-29 05:15:54 -04:00
### How aliases work
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
Using the first example above, Hugo generates the following site structure:
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
```text
public/
├── posts/
│ ├── new-file-name/
│ │ └── index.html
│ ├── previous-file-name/
│ │ └── index.html
│ └── index.html
└── index.html
```
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
The alias from the previous URL to the new URL is a client-side redirect:
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
{{< code file = "posts/previous-file-name/index.html" copy = false > }}
<!DOCTYPE html>
< html lang = "en-us" >
< head >
< title > https://example.org/posts/new-file-name/< / title >
< link rel = "canonical" href = "https://example.org/posts/new-file-name/" >
< meta name = "robots" content = "noindex" >
< meta charset = "utf-8" >
< meta http-equiv = "refresh" content = "0; url=https://example.org/posts/new-file-name/" >
< / head >
< / html >
{{< / code > }}
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
Collectively, the elements in the `head` section:
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
- Tell search engines that the new URL is canonical
- Tell search engines not to index the previous URL
- Tell the browser to redirect to the new URL
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
Hugo renders alias files before rendering pages. A new page with the previous file name will overwrite the alias, as expected.
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
### Customize
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
Create a new template (`layouts/alias.html`) to customize the content of the alias files. The template receives the following context:
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
`Permalink`
: the link to the page being aliased
2019-10-21 04:22:28 -04:00
2023-05-22 10:43:12 -04:00
`Page`
: the Page data for the page being aliased