Hexo博客优化:自定义文章URL

Hexo默认用文章标题作为URL,如果标题中含有中文就会产生大量转义字符,既不美观也不方便分享。可以设置permalink对URL进行优化。关于URL优化问题网上有大量教程,但是很多教程并不完美甚至存在错误。本人在踩坑的过程中也学习了很多关于URL和服务器端工作原理的知识,一并记录于此。

文章URL的优化

如果你的博客文章URL中包含中文,则中文会被转义成UTF-8编码1。比如浏览器中的https://zh.wikipedia.org/wiki/春節实际上会被转义成https://zh.wikipedia.org/wiki/%E6%98%A5%E7%AF%80。而Hexo框架默认的URL中含有标题信息,因此如果标题中含有中文的话URL中也会出现大量转义字符,既不美观也不方便分享。所以对URL进行美化非常重要。好在Hexo框架支持URL美化功能。

URL设置有两种思路:

  • 根据文章的某些固定信息采用特定算法生成链接
  • 为每一篇文章手动设置链接

前面一种方法常用于一些较大的内容发布平台,例如B站的视频Av、Bv号、知乎的问题号等等,优点是可以简单方便地批量管理内容,但是生成的URL并不直观。对于自己的个人博客,我还是更希望文章的URL能大致反映文章的内容,反正文章数量也不多,管理起来也不麻烦。因此我选择后面一种方法,为每篇文章手动设置URL。

手动设置URL

在hexo的配置文件_config.yml中可以修改文章的默认URL:

1
2
3
4
5
6
7
8
9
# URL
## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project'
url: https://donnadie.top
# permalink: :year/:month/:day/:title/
permalink: :link/
permalink_defaults:
pretty_urls:
trailing_index: true # Set to false to remove trailing 'index.html' from permalinks
trailing_html: false # Set to false to remove trailing '.html' from permalinks

注意上面的permalink属性已经被修改成了:link/。具体是什么意思呢?

Hexo框架支持使用:attr的格式引用文章的Front Matter中的属性,上面的link属性是我自己定义的一个属性。只要在文章的Front Matter中加上link值,Hexo在生成html文件的时候就会自动引用Front Matter中的值。例如这篇文章的Front Matter设置如下:

1
2
3
4
5
6
7
title: Hexo博客优化:自定义文章URL
date: 2021-07-11 11:51:01
tags:
- 经验总结
categories:
- 博客搭建
link: hexo-optimization-permalink

那么这篇文章的URL就会被设置为https://donnadie.top/hexo-optimization-permalink。此外,Hexo还支持很多其他选项的设置:

变量 描述
:year 文章的发表年份(4 位数)
:month 文章的发表月份(2 位数)
:i_month 文章的发表月份(去掉开头的零)
:day 文章的发表日期 (2 位数)
:i_day 文章的发表日期(去掉开头的零)
:hour 文章发表时的小时 (2 位数)
:minute 文章发表时的分钟 (2 位数)
:second 文章发表时的秒钟 (2 位数)
:title 文件名称 (relative to “source/_posts/“ folder)
:name 文件名称
:post_title 文章标题
:id 文章 ID (not persistent across cache reset)
:category 分类。如果文章没有分类,则是 default_category 配置信息。
:hash SHA1 hash of filename (same as :title) and date (12-hexadecimal)

更多信息详见官方文档

附:使用abbrlink插件

读到这里相信你已经搞懂了Hexo设置permalink的原理。如果你厌倦了手动设置permalink,推荐使用hexo-abbrlink插件。搞懂原理之后参照插件官方给出使用方法应该很容易上手。

rozbo/hexo-abbrlink: create one and only link for every post for hexo (github.com)

讨论:URL末尾的.html后缀

一开始在网上搜索URL美化的时候,网上的教程大多给出的是类似这样的配置:

1
2
# permalink: :year/:month/:day/:title/
permalink: :link.html

相信细心的你已经发现它跟前文给出的配置区别在哪了:一个是以.html结尾的,而另一个则以/结尾。以.html结尾的页面URL当然没问题,但是根据“Short is Better”的原则,一般在URL优化中还是会将最后的.html去掉。

踩坑:URL最后的那个斜杠/

既然说URL最后的那个.html后缀不美观,那么直接把后缀去掉行不行呢?

1
2
# permalink: :year/:month/:day/:title/
permalink: :link

我一开始也是这么想当然地尝试的,上网一搜也真有教程是这么教的。但是尝试之后发现不对劲,博客生成阶段就会出现类似如下报错:

1
2
3
4
5
6
7
8
FATAL {
err: [Error: EISDIR: illegal operation on a directory, open '/mnt/c/Users/z/Desktop/hexo/public/vmtools-from-cli'] {
errno: -21,
code: 'EISDIR',
syscall: 'open',
path: '/mnt/c/Users/z/Desktop/hexo/public/vmtools-from-cli'
}
} Something's wrong. Maybe you can find the solution here: %s https://hexo.io/docs/troubleshooting.html

为什么会出现这样的报错呢?这就不得不提到Hexo渲染页面的方式以及服务器和浏览器处理URL的方式了。

首先是Hexo处理生成网页的方式。对于permalink: :link.htmlpermalink: :link/这两种设置方法,Hexo生成页面的方式是不同的。

  • 对于permalink: :link.html,Hexo将在站点根目录下生成页面源文件,同时根目录下还有页面的资源文件夹。此时根据带有.html的URL访问页面服务器就会呈现页面。这也是最直接的一种请求处理方式。

    例如对于本文章,生成之后网站目录应当是如下所示的结构(已略去无关文件):

    1
    2
    3
    4
    5
    .
    ├── hexo-optimization-permalink
    │   └── img.png
    ├── hexo-optimization-permalink.html
    └── index.html
  • 对于permalink: :link/,Hexo则会在站点根目录下为页面创建一个文件夹,而文章的页面源代码则在这个文件夹下的index.html文件中。当浏览器发出请求时URL最后的斜杠/告诉服务器它请求的是一个目录,因此服务器就会到相应目录下寻找默认的index.html文件并返回给浏览器。

    同样以本文为例,生成之后网站目录如下所示(已略去无关文件):

    1
    2
    3
    4
    5
    .
    ├── hexo-optimization-permalink
    │   ├── img.png
    │   └── index.html
    └── index.html

这时候回头再看上面给出的permalink: :link的配置方法,就会发现这其实有点不伦不类了。因此综合技术上的正确性和URL的简洁性考虑,推荐采用类似permalink: :link/的配置方法。

看到这里你或许会有疑问,明明访问很多网站时我没有在URL后面加上这个斜杠,为什么也能正常显示页面呢?

这其实是现代网页服务器为了照顾用户的使用习惯而作出的兼容性措施。

对于用户给出的不带斜杠的URL,例如你在浏览器中像这样尝试访问本文:https://donnadie.top/hexo-optimization-permalink,服务器会认为你想访问一个在根目录下的名叫hexo-optimization-permalink的文件,因此会在根目录下搜索这个文件。搜索未遂时应当返回404

但是这样显然会造成很差的用户体验,毕竟一直以来对于斜杠问题傻傻分不清楚的用户不在少数。因此服务器端会对这一问题进行优化。

服务器端采取301跳转的方式处理此问题2

It is common for users to forget the trailing slash on a resource, and common for web-servers to assist users, when they make this mistake, to redirect them to the URI with the trailing slash automatically.

In fact this practice is so pervasive, that for the vast majority of users, a resource URI with or without a trailing slash is treated as a synonym. They are considered two URIs that point to the same resource, using either one is fine. However this understanding is not quite correct.

It is more correct to understand that the resource without the trailing slash does not exist at all. But instead of being unhelpful and reporting a 404 Not Found status, web-servers almost always apply Postel’s Law and tell the user where the resource they are looking for is actually located, via a permanent redirect.

比如在浏览器中输入https://donnadie.top/hexo-optimization-permalink访问本文,Chrome的开发者工具展现了如下的过程。

image-20210711191439840

重定向之后你的地址栏中的URL也会变成https://donnadie.top/hexo-optimization-permalink/

可能你会发现不少很多网站在使用不带斜杠的URL进行访问的时候也不会发生重定向3,这其实可以通过修改服务器端的配置来实现。更多关于URL中斜杠的信息可以看看参考资料4,5

对于普通用户而言并不需要了解这些技术上的细节,但是对于网站建设者而言这些细微的不同可能会导致路径解析上的巨大不同,甚至引起一些错误2,6。此外,从SEO的角度,让搜索引擎爬虫知道你偏好的是哪一种URL风格也是十分重要的7

分类和标签的优化

文章的URL都已经设置好了,但是标签页和分类页还是默认的中文。不过Hexo提供了tag_map和category_map功能处理这个问题。

在hexo的配置文件中找到tag_map,在下面添加每一个分类和标签的映射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Category & Tag
default_category: uncategorized
category_map:
技术: tech
效率: productivity
生活: life
LaTeX技巧: latex
博客搭建: blog-buildup
tag_map:
奇技淫巧: tricks
效率工具: tools
新品体验: hands-on
日常分享: sharing
疑难杂症: oddities
LaTeX技巧: latex
经验总结: experience

这样每个标签页和分类页也有简洁美观的URL啦。

[1] 关于URL编码 - 阮一峰的网络日志 (ruanyifeng.com)


[2] Why trailing slashes on URIs are important | The former blog of cdivilly (wordpress.com)

[3] seo - Difference between “/“ at end of URL and without “/“ - Webmasters Stack Exchange

[4] URI Resolution and Trailing Slashes (cdivilly.com)

[5] To slash or not to slash | Google 搜索中心博客 | Google Developers

[6] URI Resolution and Trailing Slashes (cdivilly.com)

[7] Specify your canonical | Google 搜索中心博客 | Google Developers