Hexo SEO
How to do SEO on a Hexo site — the Node.js static site generator. Hexo outputs pure HTML so content is crawlable on the first fetch, but sitemaps, robots.txt, meta descriptions, canonicals, structured data, and stable permalinks all depend on plugins and config.
Hexo is a Node.js static site generator: hexo generate compiles Markdown into pure static HTML, so Googlebot reads your full content on the first fetch with no JavaScript rendering queue. That's the crawlability floor — but it's not finished SEO. Most default themes ship no sitemap, no robots.txt, no per-post meta description, no canonical, and no structured data; you add those with _config.yml settings, front matter fields, theme edits, and plugins (hexo-generator-sitemap, hexo-generator-robotstxt, hexo-generator-feed, hexo-abbrlink). The big traps are a wrong url: in _config.yml, the date-based default permalink that breaks links on rename, and auto-generated category/tag archives that create thin duplicate content. For Baidu-facing sites, the static output removes a real crawling obstacle — though that's not a guarantee of Baidu indexing or ranking.
TL;DR — Hexo builds your blog into plain HTML files ahead of time, so Google sees your content the moment it fetches a page — no waiting on JavaScript. That’s the easy part. The catch: Hexo doesn’t add a sitemap, a
robots.txt, meta descriptions, or canonical tags on its own. You turn those on with a few plugins and config settings.
What Hexo is
Hexo is a free blogging framework built on Node.js. You write posts in Markdown,
run hexo generate, and Hexo turns everything into finished HTML files. You upload
those files to a host (GitHub Pages, Netlify, Cloudflare Pages, and so on). When
Google or a reader requests a page, they get the complete HTML right away — there’s
nothing left for the browser to build. Evidence for this claim Hexo generates static site files from source content. Scope: Hexo static generation. Confidence: high · Verified: Hexo documentation
That’s great for SEO: your text and links are in the page from the first byte, so Google doesn’t have to run any JavaScript to find your content. Hexo sits in the same family as Hugo and Jekyll — all of them pre-build everything (see Static Site Generators).
What Hexo does not do for you
Here’s where beginners get caught: a fresh Hexo site with a default theme is missing most basic SEO pieces. You usually have to add:
- An XML sitemap — install the
hexo-generator-sitemapplugin. Evidence for this claim The official Hexo sitemap generator creates sitemap.xml output. Scope: hexo-generator-sitemap plugin. Confidence: high · Verified: Hexo sitemap generator - A
robots.txt— installhexo-generator-robotstxt(or drop a file in your source folder). - Meta descriptions — add a
description:to each post, and most themes need a small template edit to actually print it. - Canonical tags — theme-dependent; the popular NexT theme can do it once you set things up.
The two settings that matter most before you launch
In your _config.yml file:
url:must be set to your real live address withhttps://(for examplehttps://yourdomain.com). Get this wrong and your sitemap, canonical, and feed links all point to the wrong place.permalink:controls your URL pattern. The default puts the date in every URL. Many people prefer a cleaner:title/. Whatever you pick, don’t change a post’s URL later without a redirect — it breaks links and loses ranking signals.
The thing most people get wrong
“It’s static HTML, so SEO is automatic.” No. Static HTML means Google can crawl your content easily — that’s a head start, not a finish line. You still need the sitemap, the meta descriptions, the canonical tags, and good content. The static part just means you won’t have the JavaScript indexing problems that single-page apps have.
Want the full version — every plugin, the NexT theme fixes, the category/tag duplicate-content trap, structured data, and Baidu targeting? Switch to the Advanced tab.
TL;DR — Hexo (
hexo generate) emits pure static HTML, so your content is in the first byte of the response — no Web Rendering Service, no Wave 2 delay. That’s the crawlability floor, not finished SEO. Default themes ship no sitemap, no robots.txt, no per-post meta description, no canonical, and no structured data; you add them via_config.yml, front matter, theme template edits, and plugins (hexo-generator-sitemap,hexo-generator-robotstxt,hexo-generator-feed,hexo-abbrlink,hexo-indexnow). The three traps that actually bite: a wrongurl:in_config.ymlbreaks canonicals/sitemap/feed; the date-based default permalink ties URLs to filenames so a rename breaks links; and auto-generated category/tag archives create thin duplicate content. For Baidu-facing sites the static output is a real advantage.
Hexo’s static output is the crawling advantage
hexo generate runs your Markdown and theme templates through a build step and writes
a folder of finished HTML, CSS, and assets. The SEO-relevant fact is when the HTML
exists: at build time, once, for everyone — not per request, and not in the browser. Evidence for this claim The Hexo generate command builds static files into the configured public directory. Scope: Hexo generate command. Confidence: high · Verified: Hexo: Commands
Google describes a two-phase process for JavaScript pages: Wave 1 fetches the raw HTML and indexes static content immediately; Wave 2 is a deferred headless-Chromium rendering queue that can take seconds to days. Hexo output skips Wave 2 entirely — every page is raw HTML on the first fetch. Google’s own guidance is blunt that this is the recommended path: dynamic rendering was “a workaround and not a long-term solution… use server-side rendering, static rendering, or hydration as a solution,” and Hexo produces static rendering, one of the three explicitly recommended approaches.
Two more wins fall out of this:
- Crawlable links by default. Hexo templates emit plain
<a href>anchors in static HTML, so they satisfy Google’s requirement that links be real, crawlable URLs — no JavaScript-generated navigation to design around. - Cheap to crawl. Static files served from a CDN have effectively zero server-processing cost per request, so Googlebot can crawl at its allowed rate without tripping crawl-rate throttling. As Gary Illyes put it, “if you are making expensive database calls, that’s going to cost the server a lot” — Hexo makes none.
The caveat: zero render-blocking for crawlers is not the same as zero render-blocking for users. Theme JS/CSS still affects Core Web Vitals (more below).
Configure _config.yml first — url: is the big one
Set these global keys before your first deploy:
url: https://yourdomain.com # must match your live domain, with https://
permalink: :title/ # or :year/:month/:day/:title/ (the default)
title: Site Title
description: Global meta description fallback
author: Your Name
language: en
meta_generator: false # removes the <meta name="generator" content="Hexo"> tagurl:is the single most consequential setting. It’s the site’s configured base URL, which themes and generators can use for canonical tags, feeds, sitemaps, and pagination. A wrong value can propagate into those outputs. Evidence for this claim Hexo's url configuration supplies the website URL used by themes and generators. Scope: Exact downstream fields depend on the installed theme and plugins. Confidence: high · Verified: Hexo: Configurationpermalink:— the default:year/:month/:day/:title/produces date-based URLs that look stale over time and tie the slug to the filename. Cleaner options are/:title/or/:category/:title/.meta_generator: falsestrips Hexo’s generator fingerprint from<head>.- Deploying to a subdirectory?
url:alone isn’t enough — setroot:to the subdirectory path too (e.g.root: /blog/). Hexo’surl_forhelper builds root-relative links, whilefull_url_forprepends the fullurl:value; getroot:wrong on a subdirectory deploy and internal links, feeds, and sitemap entries end up pointing at the domain root instead of the actual path. - Config precedence: the site’s
_config.ymlsets the baseurl/root/permalinkvalues; a theme’s own config (its bundled_config.yml, or a_config.<theme>.ymlat the project root) layers on top for display and template settings, but doesn’t override the site-level URL structure — a theme can’t fix a wrongurl:. - Testing in safe mode?
hexo server --safe(orhexo --safe) disables every plugin and script, so the output you’re viewing is missing your sitemap, robots.txt, and any SEO plugin’s tags. Don’t use a safe-mode preview to check SEO output — run a normalhexo generatefirst.
Front matter SEO controls (and what’s missing)
Native Hexo front matter you can rely on:
---
title: "Article Title for the <title> tag"
date: 2024-01-15
updated: 2024-06-01
tags: [tag1, tag2]
categories: [Category]
permalink: /custom-url/ # overrides the global pattern for this post
published: true
sitemap: false # exclude this post from sitemap.xml
---What is not in native front matter and needs theme support or a template edit:
description(the meta description) — most themes don’t print it without a patch.keywords(per-post keyword meta) — and largely pointless anyway; Google has ignored<meta name="keywords">since 2009. Treatkeywordsas an organizational aid, not a ranking lever.canonical(override the canonical URL) — theme-dependent.
Keep the updated: field current when you revise a post — sitemap freshness depends
on it (next section).
Essential plugins
Sitemap — hexo-generator-sitemap (official):
npm install hexo-generator-sitemap --savesitemap:
path:
- sitemap.xml
tags: true
categories: trueIt reads lastmod from each post’s updated field (falling back to date) — which
matters because Bing warns that lastmod “should reflect the true last modification
time of the page content, not the sitemap file itself.” After hexo generate, submit
the resulting sitemap.xml in Google Search Console and Bing Webmaster Tools. And
heed Bing’s other warning: “Do not submit a static sitemap and forget it. New pages
won’t get picked up” — for Hexo that means re-deploying and re-submitting (or using
IndexNow) after every new post.
robots.txt — hexo-generator-robotstxt:
npm install hexo-generator-robotstxt --saverobotstxt:
useragent: "*"
allow:
- /
disallow:
- /private/
sitemap: https://yourdomain.com/sitemap.xmlWithout it, Hexo produces no robots.txt at all — no sitemap pointer for crawlers.
RSS — hexo-generator-feed: relevant for aggregators and Google News eligibility;
John Mueller includes an RSS feed on his own static site.
IndexNow — hexo-indexnow: Bing recommends pairing sitemaps with IndexNow for
real-time, URL-level submission. Drop the generated key file into source/ so it
deploys as yourdomain.com/<key>.txt. Note this is for Bing, Yandex, and others —
not Google, which doesn’t use IndexNow for general pages.
Permalink stability — the abbrlink fix
The default permalink has two SEO problems: renaming the Markdown file changes the URL
(because the slug comes from the filename), and non-Latin titles (Chinese, emoji)
produce garbled URLs. hexo-abbrlink decouples the URL from the filename by writing a
stable hash back into front matter:
npm install hexo-abbrlink --savepermalink: posts/:abbrlink/
abbrlink:
alg: crc32 # crc16 or crc32
rep: hex # dec or hexYou get something like /posts/3a9c2b1d/ that survives file renames, category
changes, and date edits. The underlying rule stands either way: once a URL is
published, don’t change it without a 301 redirect — a rename without one destroys
link equity and any accumulated ranking signals.
Two more permalink pitfalls worth knowing before you pick a pattern:
- Don’t use the
:idtoken. Hexo documents post IDs as not persistent across a database cache reset — if your permalink pattern includes:id, a cache rebuild can silently change every post’s URL. Stick to:title,:abbrlink, or another stable token. trailing_indexandtrailing_htmlare separate controls, not one setting. Hexo’spretty_urlsconfig toggles them independently — one governs whether a trailingindex.htmlis stripped from generated links, the other whether a trailing slash is added. If these don’t match how your host actually resolves URLs (some hosts redirect/postto/post/, others don’t), you can end up with internal links, canonicals, and sitemap entries disagreeing with the URL the server actually serves.
NexT theme SEO configuration
NexT is the most-used Hexo theme, configured in _config.next.yml:
canonical: true # builds canonical from url: + page path
webmaster_tools:
google: VERIFICATION_STRING
bing: VERIFICATION_STRING
baidu: VERIFICATION_STRING
open_graph:
enable: true
twitter_card: summary_large_imageThe NexT meta-description gap: NexT does not automatically inject a per-post
<meta name="description"> from the front matter description:. You patch the theme’s
head template to fall back from page.description to the site description. This is the
single most common NexT SEO miss. (NexT does support canonical: true and built-in
Open Graph / Twitter Cards, so those at least work once enabled.)
For non-NexT themes, you usually create or edit a head partial to emit og:title,
og:description, og:image, og:url, and twitter:card — support varies widely by
theme.
Who owns each tag when a theme and a plugin both emit it. A theme’s built-in
Open Graph/canonical support and an all-in-one plugin like hexo-seo (which
generates its own breadcrumb, website, and article schema) can both try to write the
same tag. Turning both on produces duplicate or conflicting <meta> tags, canonicals,
or JSON-LD blocks in the same <head>. Pick one layer as the owner for each tag
category — theme or plugin, not both — and disable the corresponding feature on the
other before you ship.
Category and tag pages: the duplicate-content trap
Hexo auto-generates an archive page for every category and every tag, each filled with post excerpts that also appear on the posts themselves. At scale that’s hundreds of thin pages competing for the same queries and diluting crawl attention. Your options:
sitemap: falseper archive (impractical at scale).- Theme-level
noindexon the category/tag archive templates. - Keep them indexed only if a category is a genuine, curated topic hub with real value.
- Self-referencing canonical on each paginated archive page (page 2 canonicalizes to itself, not page 1).
One caveat on option 2: long-term noindex eventually makes Google stop following the
links on that page, so noindexing tag pages can reduce crawling of posts that are only
linked from those tags. Make sure such posts are reachable some other way.
Your route inventory is bigger than source/. Category, tag, archive, and
pagination pages don’t come from Markdown files — Hexo’s generator API is what
actually creates them, and themes or plugins can register their own generators too.
That means the crawlable URL surface can silently expand beyond what you’d expect
from browsing your source/ folder. After installing or updating a theme or plugin,
run hexo generate and diff the route list in public/ rather than assuming you
know every URL Hexo is producing.
Structured data / JSON-LD
Hexo injects no structured data by default. Either add a BlogPosting JSON-LD block to
your theme’s post template (headline, datePublished, dateModified, author,
publisher, url) or use an all-in-one plugin like hexo-seo that generates
breadcrumb, website, and article schema for you.
Core Web Vitals
Static output is fast, but themes can undo that. NexT and many themes bundle large
JS/CSS and icon-font libraries (Font Awesome is a frequent LCP/render-blocking
culprit). Audit with Lighthouse, then reach for hexo-all-minifier (HTML/CSS/JS
compression at build time), hexo-lazyload-image, and defer/async on third-party
scripts like analytics and ads.
Targeting Baidu (Chinese-audience sites)
Baidu’s crawler handles JavaScript poorly, so Hexo’s static HTML removes one obstacle
to Baidu discovery. Add baidu_site_verification in NexT, generate a Baidu-format
sitemap with hexo-generator-baidu-sitemap, and push URLs actively with
hexo-submit-urls-to-search-engine. One hosting note: GitHub Pages is blocked in
mainland China — use Gitee Pages, Cloudflare, or a Chinese CDN for Baidu traffic.
Treat that as a crawlability advantage, not a guarantee. Static HTML doesn’t by itself prove Baidu is discovering, indexing, or ranking your pages — confirm actual crawl activity and indexing status directly in Baidu Ziyuan (Baidu’s webmaster tools) rather than assuming static output alone secures visibility.
Deploying to GitHub Pages — the SEO gotchas
- Set
url:to your live address (custom domain withhttps://) before the first deploy. - Put a
CNAMEfile insource/for custom domains so it isn’t reset on everyhexo deploy. - Enforce HTTPS in repo Settings → Pages after DNS propagates.
- Prefer a GitHub Actions workflow over
hexo-deployer-gitto avoid CNAME-reset issues.
Hexo vs. Hugo vs. Jekyll vs. Astro, for SEO
All four ship pure static HTML, so the indexability floor is identical — there’s no framework-level SEO disadvantage in Hexo. The differences are operational: Hugo (Go) builds far faster at large scale and has a built-in sitemap; Jekyll is the GitHub Pages default; Astro ships zero JS by default via islands. Hexo’s edge is a rich npm plugin ecosystem and a large Chinese-language community. Content quality, not the generator, decides rankings — as Mueller said of switching generators, “Will your site’s SEO suddenly explode? No. Also No.”
For the cluster overview and the other generators, see Static Site Generators; for the broader rendering picture, see JavaScript SEO.
AI summary
A condensed take on the Advanced version:
- Hexo is a Node.js static site generator.
hexo generateoutputs pure static HTML, so content is in the first byte of the response — no Web Rendering Service, no Wave 2 rendering-queue delay. Crawlable<a href>links and cheap-to-crawl CDN files come free. - Static HTML is the crawlability floor, not finished SEO. Default themes ship no sitemap, no robots.txt, no per-post meta description, no canonical, and no structured data.
- Configure
_config.ymlfirst.url:is the most consequential setting — it feeds canonicals, sitemap, RSS, and pagination; a wrong value breaks all of them. Subdirectory deploys also needroot:set. Pick a cleanpermalink:(avoid the:idtoken — it isn’t persistent across a cache reset) and setmeta_generator: false. A theme’s own config can’t override the site-levelurl/root/permalink, and a safe-mode preview (--safe) disables every plugin and script, so don’t check SEO output there. - Plugins do the heavy lifting:
hexo-generator-sitemap(readslastmodfrom theupdatedfield),hexo-generator-robotstxt,hexo-generator-feed,hexo-indexnow(Bing/Yandex, not Google),hexo-abbrlinkfor stable permalinks. - NexT theme: supports canonical + Open Graph, but does not auto-print a per-post meta description — a template patch is the most common fix. If a theme and an SEO plugin can both emit the same tags, enable only one per tag category to avoid duplicates.
- Three traps: wrong
url:; the date-based default permalink that breaks links on rename (never change a URL without a 301); and auto-generated category/tag archives that create thin duplicate content (noindex or canonicalize). Those archive/tag/ pagination routes come from Hexo’s generator API, notsource/files — a theme/plugin update can silently expand the indexable URL surface, so diffpublic/after changes. - Baidu: static output removes a real obstacle (Baidu’s crawler handles JS poorly), but it’s a crawlability advantage, not a guarantee of indexing or ranking — confirm in Baidu Ziyuan. GitHub Pages is blocked in China — use Gitee/Cloudflare/a Chinese CDN.
- Vs. Hugo/Jekyll/Astro: same indexability floor; Hexo’s edge is the npm plugin ecosystem and Chinese community. Content quality decides rankings, not the generator.
Official documentation
Primary-source documentation — from Hexo, the search engines, and the key plugins.
Hexo
- Hexo documentation — the framework overview.
- Configuration —
url,permalink,meta_generator, and the rest of_config.yml. - Permalinks — URL pattern variables and tradeoffs.
- Front matter — the per-post fields Hexo supports natively.
- GitHub Pages deployment — the official deploy guide.
Plugins
- hexo-generator-sitemap — the official sitemap plugin.
- hexo-abbrlink — stable hash-based permalinks.
- NexT theme SEO settings — canonical, webmaster verification, Open Graph.
Search engines — rendering, links, sitemaps
- JavaScript SEO basics — the crawl → render → index phases a static build lets you skip for content.
- Dynamic rendering (deprecated) — Google recommending static rendering instead.
- Make your links crawlable — the
<a href>requirement Hexo satisfies by default. - SEO Guide for Web Developers — titles, descriptions, canonicals, sitemaps, structured data.
- Bing — Keeping Content Discoverable with Sitemaps — XML preference,
lastmodaccuracy, and IndexNow.
Quotes from the source
On-the-record statements from Google, Bing, and John Mueller that bear directly on a Hexo (static) site.
Google — static rendering is the recommended path
- “Dynamic rendering was a workaround and not a long-term solution for problems with JavaScript-generated content in search engines… Instead, we recommend that you use server-side rendering, static rendering, or hydration as a solution.” — Hexo produces static rendering. Source
- “Server-side or pre-rendering is still a great idea because it makes your website faster for users and crawlers, and not all bots can run JavaScript.” Source
Bing — sitemaps and freshness
- “XML remains the preferred format for sitemaps” — because it supports structured metadata like
lastmodfor freshness assessment. Source - On
lastmod: it “should reflect the true last modification time of the page content, not the sitemap file itself.” — the Hexo gotcha that theupdatedfield must be kept current. Source - “By combining sitemaps for comprehensive site coverage with IndexNow for fast, URL-level submission, you provide the strongest foundation for keeping your content fresh, discoverable, and visible.” Source
Gary Illyes, Google — static sites are cheap to crawl
- “If you are making expensive database calls, that’s going to cost the server a lot.” — static HTML from a CDN makes none. Coverage
John Mueller — switching generators isn’t itself an SEO win
- “Will this change how your site appears in all search engines? Will your site’s SEO suddenly explode? No. Also No.” — content quality, not the generator choice, drives rankings. Source
Hexo SEO checklist
A pass to confirm a Hexo site is search-friendly — most of these are off by default:
-
url:in_config.ymlis your live domain withhttps://(not a placeholder, nothttp://). Deploying to a subdirectory?root:is set too. - A permalink pattern is chosen and won’t churn — ideally
:title/orhexo-abbrlinkfor stable hashes. The:idtoken is not used (it isn’t persistent across a database cache reset). - If both a theme and an SEO plugin can emit canonical/OG/JSON-LD tags, exactly one of them is enabled for each tag category — not both.
-
meta_generator: falseset to drop the Hexo fingerprint (optional but tidy). -
hexo-generator-sitemapinstalled;sitemap.xmlgenerates and is submitted in Google Search Console and Bing Webmaster Tools. -
updated:front matter is kept current so sitemaplastmodis accurate. -
hexo-generator-robotstxtinstalled (or a manualrobots.txtinsource/) with the sitemap pointer. - Each post has a
description:, and the theme actually prints<meta name="description">(patch NexT if needed). - Canonical tags are emitted and resolve to live HTTPS URLs (NexT:
canonical: true). - Open Graph / Twitter Card tags present (NexT built-in, or a head-partial edit).
-
BlogPostingJSON-LD added (template snippet orhexo-seoplugin). - Category/tag archives handled — noindexed, canonicalized, or kept only as real topic hubs (not left as thin duplicates).
- Paginated archive pages self-canonicalize (page 2 → page 2, not page 1).
- Theme JS/CSS audited in Lighthouse; minifier + lazy-load image plugins considered.
- GitHub Pages:
CNAMEinsource/, HTTPS enforced, deploy via Actions to avoid CNAME reset. - Baidu sites: not on GitHub Pages; Baidu verification + Baidu sitemap + URL push configured.
- No URL ever changed without a 301 redirect in place.
Hexo SEO — cheat sheet
On by default vs. you must add it
| SEO feature | Hexo default | How to get it |
|---|---|---|
| Static HTML / crawlable content | Yes | hexo generate |
Crawlable <a href> links | Yes | theme templates |
| XML sitemap | No | hexo-generator-sitemap |
| robots.txt | No | hexo-generator-robotstxt or manual file |
| RSS feed | No | hexo-generator-feed |
| Per-post meta description | No (theme-dependent) | description: + theme patch |
| Canonical tag | No (theme-dependent) | NexT canonical: true |
| Open Graph / Twitter Card | No (theme-dependent) | NexT built-in or head-partial edit |
| Structured data (JSON-LD) | No | template snippet or hexo-seo plugin |
| Stable permalinks | No (date/filename based) | hexo-abbrlink |
| IndexNow submission | No | hexo-indexnow (Bing/Yandex, not Google) |
Key _config.yml settings
| Setting | Why it matters |
|---|---|
url: | Base for canonical, sitemap, RSS, pagination — wrong value breaks all of them |
root: | Subdirectory path for a subdirectory deploy — required alongside url: or root-relative links break |
permalink: | Default is date-based; :title/ is cleaner; never change after publishing; avoid the non-persistent :id token |
meta_generator: false | Removes the <meta name="generator" content="Hexo"> tag |
Plugin install commands
npm install hexo-generator-sitemap --save
npm install hexo-generator-robotstxt --save
npm install hexo-generator-feed --save
npm install hexo-abbrlink --save
npm install hexo-indexnow --saveFast facts
- Hexo output skips Google’s Wave 2 rendering queue — content is in the first fetch.
lastmodcomes from theupdatedfield (falls back todate) — keep it current.<meta name="keywords">is ignored by Google (since 2009) — organizational only.- Category/tag archives are auto-generated and thin → noindex or canonicalize.
- Baidu can’t crawl JS well → static Hexo is an advantage, but GitHub Pages is blocked in China.
Build, serve, and verify a Hexo site
The basic Hexo build loop is the same everywhere; the shell differs.
macOS / Linux
# install dependencies and the SEO plugins
npm install
npm install hexo-generator-sitemap hexo-generator-robotstxt hexo-generator-feed --save
# clean stale output, then build the static HTML
npx hexo clean && npx hexo generate
# preview locally before deploying
npx hexo server
# confirm the sitemap and robots.txt were actually generated
ls public/sitemap.xml public/robots.txtWindows (PowerShell)
# install dependencies and the SEO plugins
npm install
npm install hexo-generator-sitemap hexo-generator-robotstxt hexo-generator-feed --save
# clean stale output, then build the static HTML
npx hexo clean; npx hexo generate
# preview locally before deploying
npx hexo server
# confirm the sitemap and robots.txt were actually generated
Get-ChildItem public\sitemap.xml, public\robots.txtConfirm content is in the raw HTML (not JS-rendered)
The whole point of Hexo is that content is already in the HTML. Verify it by checking that your post text appears in the raw response, before any JavaScript runs.
macOS / Linux
# fetch the raw HTML and search for a phrase from your post body
curl -s https://yourdomain.com/your-post/ | grep -i "a sentence from your post"
# confirm the canonical tag points at the live HTTPS URL
curl -s https://yourdomain.com/your-post/ | grep -i 'rel="canonical"'Windows (PowerShell)
# fetch the raw HTML and search for a phrase from your post body
(Invoke-WebRequest https://yourdomain.com/your-post/).Content | Select-String "a sentence from your post"
# confirm the canonical tag points at the live HTTPS URL
(Invoke-WebRequest https://yourdomain.com/your-post/).Content | Select-String 'rel="canonical"'If the phrase shows up in curl/Invoke-WebRequest output, it’s in the static HTML
and Google sees it on Wave 1 — exactly what you want.
Patrick's relevant free tools
- Google Index Checker — Check one URL’s observable indexability blockers, or reconcile sitemap, crawl, and supplied Search Console evidence across a URL set before verifying Google’s actual state in URL Inspection.
- XML Sitemap Validator — Paste, upload, or fetch a sitemap by URL — errors, warnings, and a health score with line numbers. Pasted and uploaded sitemaps are validated entirely in your browser.
- hreflang Generator + Linter — Enter your URL × locale matrix and get bidirectional hreflang markup as head tags, sitemap XML (auto-split past 50,000 URLs), and Link headers — linted live for wrong region codes, duplicates, and missing fallbacks. Runs entirely in your browser.
Tools for Hexo SEO
Hexo plugins (the SEO toolkit)
- hexo-generator-sitemap — the official XML sitemap generator; reads
lastmodfrom theupdatedfield. - hexo-generator-robotstxt — generates
robots.txtwith a sitemap pointer. - hexo-generator-feed — RSS/Atom feed for aggregators and Google News eligibility.
- hexo-abbrlink — stable, hash-based permalinks that survive file renames.
- hexo-indexnow / hexo-submit-urls-to-search-engine — push changed URLs to Bing (IndexNow) and Baidu respectively.
- hexo-all-minifier / hexo-lazyload-image — Core Web Vitals helpers.
- hexo-seo — all-in-one option with structured-data generation.
Testing and monitoring
- Google Search Console — submit the sitemap, inspect URLs, watch indexing.
- Bing Webmaster Tools — submit the sitemap, configure IndexNow, run Site Scan.
- Baidu Webmaster (Ziyuan) — verification and URL push for Chinese-audience sites.
- Lighthouse / PageSpeed Insights — audit theme JS/CSS for Core Web Vitals regressions.
- View Source / curl — confirm content, canonical, and meta description are in the raw HTML (see the Scripts tab).
Hexo SEO mistakes to avoid
Concrete mistakes people actually make when they set up or migrate a Hexo site — prevention, not diagnosis (see the Troubleshooting tab for that).
Leaving url: as the placeholder or on http://
Publishing with url: http://yoursite.com still set to the scaffold default or the
wrong protocol is why it’s wrong: canonical tags, the sitemap, RSS links, and
pagination all build off this one value, so every one of them silently points at the
wrong address. What to do instead: set url: to your real live domain with https://
before your first hexo generate, and re-check it any time you move domains.
Renaming a post file (or changing its slug) with no redirect
The default permalink derives the URL from the post’s filename and date, so renaming
the Markdown file or editing the date changes the live URL. Why it’s wrong: every
inbound link and any accumulated ranking signal on the old URL is now pointing at a
404. What to do instead: switch to hexo-abbrlink for a stable hash-based permalink
before you have links to lose, and if you ever must change a published URL, add a 301
redirect at your host or CDN.
Shipping without hexo-generator-sitemap and hexo-generator-robotstxt
Assuming a static site generator “just handles” crawler basics is why it’s wrong: a
default Hexo install produces neither a sitemap nor a robots.txt — there’s no
sitemap pointer for crawlers to find. What to do instead: install both plugins,
confirm public/sitemap.xml and public/robots.txt exist after hexo generate, and
submit the sitemap in Search Console and Bing Webmaster Tools.
Leaving category and tag archives open to indexing at scale
Letting Hexo’s auto-generated category/tag pages accumulate as the post count grows is why it’s wrong: each archive is filled with post excerpts that duplicate the posts themselves, so you end up with hundreds of thin pages competing for the same queries and diluting crawl attention. What to do instead: noindex the archive templates, self-canonicalize paginated archive pages, or keep a category indexed only when it’s a genuine, curated topic hub.
Assuming hexo-indexnow covers Google too
Installing IndexNow and expecting it to speed up Google indexing is why it’s wrong: IndexNow is a Bing/Yandex/Baidu protocol — Google doesn’t consume it for general pages, so you still need a submitted sitemap for Google-side discovery. What to do instead: use IndexNow for Bing/Yandex freshness and rely on the sitemap plus normal crawling for Google.
Deploying to GitHub Pages without a durable CNAME
Relying on hexo-deployer-git alone for a custom domain is why it’s wrong: some
deploy paths reset or omit the CNAME file, which silently drops your custom domain
back to the default github.io address (and breaks HTTPS enforcement). What to do
instead: keep a CNAME file in source/ so it ships with every build, and prefer a
GitHub Actions workflow that publishes the built public/ folder intact.
Common Hexo SEO problems
Symptom-first lookup for issues you already have on a Hexo site.
Sitemap.xml returns a 404 after deploy
Symptom: https://yoursite.com/sitemap.xml 404s, or Search Console reports it
couldn’t fetch the sitemap.
Likely cause: hexo-generator-sitemap isn’t installed, isn’t listed in
_config.yml, or the public/ build output wasn’t fully deployed (a partial upload
or a deploy step that excludes dotfiles/XML).
Fix + check: run npm install hexo-generator-sitemap --save, add a sitemap:
block to _config.yml, then run hexo clean && hexo generate and confirm
public/sitemap.xml exists locally before deploying. After redeploying, fetch the
live URL directly (curl -I https://yoursite.com/sitemap.xml) and confirm a 200.
Google Search Console shows pages as “Discovered — currently not indexed”
Symptom: Posts are crawlable and load fine in a browser, but Search Console’s Page Indexing report leaves them in “Discovered, not indexed” for weeks.
Likely cause: thin category/tag archive pages are eating a disproportionate share of crawl attention, or the posts themselves are thin/duplicative of the archives that excerpt them.
Fix + check: noindex or canonicalize the auto-generated category/tag archives (see the anti-patterns entry above), request indexing on a handful of affected URLs in Search Console, and re-check the Page Indexing report after a few days to a couple of weeks — this isn’t an immediate signal.
Meta description isn’t showing up in search snippets
Symptom: your post has a description: field in front matter, but Google shows an
auto-generated snippet instead, or view-source: shows no
<meta name="description"> tag at all.
Likely cause: the theme’s head template never prints page.description — this is
the single most common NexT gap, and it affects other themes too.
Fix + check: patch the theme’s head partial to output
<meta name="description" content="{{ page.description or config.description }}">
(NexT-style templating), rebuild, then curl -s https://yoursite.com/your-post/ | grep -i 'name="description"' to confirm the tag is present in the raw HTML before you wait
on a snippet refresh.
Canonical tag is missing or points at the wrong URL
Symptom: curling a post shows no rel="canonical" tag, or it resolves to http://
instead of https://, or to a stale domain.
Likely cause: the theme’s canonical feature isn’t enabled (NexT needs
canonical: true in _config.next.yml), or url: in the main _config.yml is wrong
— canonical tags build off that base URL.
Fix + check: confirm url: is correct first, enable the theme’s canonical option,
rebuild, and check with curl -s https://yoursite.com/your-post/ | grep -i 'rel="canonical"' — the value should exactly match the live HTTPS URL.
Custom domain reverts to username.github.io after a deploy
Symptom: a working custom domain on GitHub Pages stops resolving after a routine
hexo deploy, falling back to the default github.io address.
Likely cause: the deploy process overwrote or omitted the CNAME file that GitHub
Pages reads to know your custom domain.
Fix + check: put (or restore) a CNAME file containing just your domain in
source/ so it’s copied into every build, redeploy, and confirm in the repo’s
Settings → Pages that the custom domain field still shows your domain with HTTPS
enforced.
Baidu isn’t indexing the site at all
Symptom: Baidu Ziyuan (Baidu Webmaster) shows little to no crawling activity even though Google indexes the site fine.
Likely cause: the site is hosted on GitHub Pages, which is blocked in mainland China, so Baidu’s crawler often can’t reach it consistently.
Fix + check: move Baidu-facing hosting to Gitee Pages, Cloudflare, or a Chinese
CDN, add Baidu site verification and a Baidu-format sitemap
(hexo-generator-baidu-sitemap), then watch the Baidu Ziyuan crawl-frequency report
over the following weeks for renewed activity.
Test yourself: Hexo SEO
Five quick questions on doing SEO with the Hexo static site generator. Pick an answer for each, then check.
Resources worth your time
My related writing
- JavaScript SEO: A Definitive Guide — rendering, DOM parity, and why static/pre-rendered output is the low-risk end of the spectrum.
- The Beginner’s Guide to Technical SEO — where rendering architecture fits in the bigger picture.
My speaking
- How Search Works (SlideShare) — my walkthrough of crawling, rendering, indexing, and ranking. (My standing disclaimer applies: “This is my understanding of systems… not going to be 100% complete or accurate.”)
From around the industry
- Hexo documentation — the official docs for config, permalinks, front matter, and deployment.
- hexo-generator-sitemap — the official sitemap plugin README.
- NexT theme SEO settings — canonical, webmaster verification, and Open Graph for the most-used theme.
- Google — JavaScript SEO basics — the crawl → render → index phases a static build lets you skip for content.
- Bing — Keeping Content Discoverable with Sitemaps — XML preference,
lastmodaccuracy, and IndexNow. - John Mueller — Some design decisions — how he runs his own static blog (sitemap + RSS + Article structured data), and why switching generators isn’t itself an SEO win.
Hexo SEO
Hexo SEO is the practice of optimizing sites built with Hexo, the Node.js static site generator. Hexo outputs pure static HTML at build time, so content is crawlable on the first fetch — but sitemaps, robots.txt, meta descriptions, canonicals, and structured data all depend on plugins and theme configuration.
Related: JavaScript SEO
Hexo SEO
Hexo is an open-source, Node.js-based static site generator and blogging framework that compiles Markdown and theme templates into pure static HTML at build time. Every page produced by hexo generate is a pre-rendered HTML file — no database queries at request time, no server-side rendering, and no client-side JavaScript needed to expose content to crawlers. That puts Hexo in the same “fully pre-rendered” category as Hugo and Jekyll: Googlebot gets complete HTML on the first fetch, with no rendering queue (Wave 2) delay.
Hexo SEO is the work of making that static output actually rank. The static HTML is a crawlability floor, not a finished SEO setup. Out of the box, most Hexo themes ship no XML sitemap, no robots.txt, no per-post meta description, no canonical tag, and no structured data. Each of those is added through _config.yml settings, front matter fields, theme template edits, or npm plugins — hexo-generator-sitemap for sitemaps, hexo-generator-robotstxt for robots.txt, hexo-generator-feed for RSS, and hexo-abbrlink for stable permalinks.
The recurring gotchas are configuration ones: the url: value in _config.yml must match your live HTTPS domain or canonicals and sitemap links break; the default :year/:month/:day/:title/ permalink ties URLs to filenames and dates, so a rename breaks links without a 301; and auto-generated category and tag archives can create thin or duplicate content. Hexo is also popular in the Chinese developer community, where its static output removes a real obstacle for Baidu, whose crawler handles JavaScript poorly — though that’s a crawlability advantage, not proof of Baidu indexing or ranking.
Related: JavaScript SEO
Build-time retrieval analysis plus live signals for this exact article. The automatic chunk report includes a deterministic readiness score and is ready without a model download.
Search Console
sampleGA4 traffic (28d)
sampleCloudflare traffic (7d)
sampledCrUX field data (28d, phone)
sampleGoogle NLP entities
localChangelog
Revision history
Compare the published article with an archived editorial snapshot. Added and removed words are shown only after you open a comparison.
Updated Jul 18, 2026.
Editorial summary and recorded change details.Summary
Added Hexo config/permalink/theme details that top-ranking pages miss: root: for subdirectory deploys, url_for vs full_url_for, the non-persistent :id permalink token, the separate trailing_index/trailing_html controls, theme-vs-plugin tag-ownership conflicts, and the generator API's route inventory — plus a hedge on the Baidu-indexing claim.
Change details
-
Documented root: for subdirectory deployments and the url_for/full_url_for distinction in the _config.yml section.
-
Added the non-persistent :id permalink token warning and the trailing_index/trailing_html distinction to the Permalinks section.
-
Added guidance on picking one owner (theme or plugin) per metadata tag category to avoid duplicate canonical/OG/JSON-LD output.
-
Explained that category/tag/pagination routes come from Hexo's generator API, not source/ files, and can expand silently after a theme or plugin update.
-
Hedged the Baidu section: static output is a crawlability advantage, not proof of Baidu indexing or ranking; verify directly in Baidu Ziyuan.
Was this helpful?
People also ask
Keep the conversation going
Ask a question here or ask other technical SEOs
My notes
- Jekyll SEO
- Static Site Generators
- Hexo SEO
Glossary: Hexo SEO