days since this article was written, please be aware of its timeliness
Preface
Earlier, I wrote an article titled Notes on Domain Optimization Configuration for This Blog], some of the methods mentioned in it are no longer in use. Meanwhile, I’ve added new techniques and applied them to this Jekyll-based blog. Some details require attention, hence this post.
CDN Services
Previously, the CDN service involved using Baidu CDN and Qiniu’s static resource hosting]. The approach was to resolve img.xheldon.com to Qiniu Cloud and then upload static resources there. However, due to well-known reasons, Qiniu’s free personal CDN service now requires ICP备案 (Chinese domain registration) and must be hosted on domestic servers, so I abandoned this solution.
For a long time, I simply placed images and static resources directly in the project. Since GitHub’s free repositories have a 1 GB total size limit, I avoided using images whenever text could suffice, which was quite restrictive. Recently, I stumbled upon another GitHub Pages-based blog that heavily used images, with links related to jsdelivr.net. I suspected it might be using a free overseas CDN service. After some research, I confirmed this was the case—it even had servers in China with free acceleration.
By prepending a fixed path to resources in the GitHub repository, the CDN would store them on S3 upon the first request. Even if GitHub went down afterward, the links would still work. If the resource wasn’t on S3, the CDN would fall back to fetching it directly from GitHub—standard CDN behavior.
I was thrilled—a free CDN with acceleration, seemingly straightforward to implement. One of jsDelivr’s selling points is being a foreign public CDN provider with a legitimate ICP license in China, boasting hundreds of servers nationwide.

So I got to work. Here’s what I did:
Add a Dedicated Static Resource Repository
This step ensures jsDelivr CDN can correctly resolve resources. For example, if a local image’s reference path is /static/img/in-post/logo.png, its unique path becomes /img/logo.png. After pushing it to a GitHub repository, the access path would be https://github.com/Xheldon/x_blog-static/tree/master/img/logo.png. My username is xheldon, and the repository is x_blog-static, so the jsDelivr CDN URL would be https://cdn.jsdelivr.net/gh/xheldon/x_blog-static/img/logo.png. No registration or configuration is needed—just plug and play. Awesome!
Modifications to the Blog Repository
Key steps include:
- Delete the
staticfolder. After pushing its contents to the new GitHub resource repositoryx_blog-static, it can be removed. - Add the resource repository
x_blog-staticas a submodule to the blog repository:git submodule add https://github.com/Xheldon/x_blog-static.git ./static, and place the static resources (previously in thestaticfolder) under thestaticdirectory. - Modify the
_config.ymlfile to add a configuration objectstatic_url: https://cdn.jsdelivr.net/gh/xheldon/x_blog-static, simplifying the process of updating static resource absolute paths. - Add a local development config file
_config.dev.ymlwith a configuration objectstatic_url: /static, again for easier path updates. - Globally replace local absolute paths for static resources. For example, change
imgsrcvalues like/static/img/in-post/logo.pngor/static/css/main.cssto `https://static.xheldon.cn/img/logo.png`. Note that Liquid syntax tags like `{{}}` work even in Markdown files—very handy. - Keep frequently modified files like
main.scsslocally for reasons explained below. - Push the changes to the remote repository.
Known Issues
Solving some pain points introduced others. Since Jekyll supports SCSS, you can write Sass syntax in resoruce/css/main.scss and reference it directly in HTML as resoruce/css/main.css (note the file extension). Jekyll automatically converts SCSS to CSS. However, this means the stylesheet must remain local and cannot be hosted remotely.
To address this, I kept frequently modified files like CSS and JS in the blog repository instead of the static resource repository.
Another note: I initially wanted to define a global Liquid variable like static_url in the default layout template and use it in Markdown files like `{{static_url}}/img/logo.png`. However, after testing, this didn’t work. Research revealed that neither Liquid, Jekyll, nor GitHub Pages supports defining variables at the level of site, pages, or paginator.
So I thought about directly defining a variable in the default layout file assign, then tried accessing it in the markdown file that uses the default layout within the post layout, but it still didn’t work. I also attempted to use Jekyll’s supported environment variables JEKYLL_ENV=dev to achieve this, but like before, since variables cannot be shared between layouts (regardless of nesting relationships), I had to abandon the idea. After some research, I found the reason: Jekyll first renders the content before rendering the layout files, so markdown files cannot access variables from external layouts.
Actually, I could have added a template snippet before each markdown file to check the value of jekyll.environment and then assign a variable like static_url, which could later be used in the markdown. However, I really didn’t want to modify all markdown files by adding such an awkward logic snippet in the header. Similarly, I could have added environment checks everywhere the static_url variable is used. For example, to include an image, I’d have to write something like this (pseudo-code): `{{if environment=dev 'static' else 'https://jsdelivr.net/xxx/'}}/img/logo.png` , which is even more cumbersome, so I gave up on that approach.
Therefore, the final solution was to create two Jekyll YAML configuration files: one default _config.yml for GitHub Pages’ online page generation, and another _config.dev.yml for local development. The only difference between them is a configuration value called static_url. In _config.dev.yml, its value is /static, while in _config.yml, it’s https://cdn.jsdelivr.net/gh/xheldon/x_blog-static.
It’s worth noting that after publishing, some images might return a 403 error: Package size exceeded the configured limit of 50 MB. Try https://github.com/xxx instead. To resolve this, I needed to append a version tag or branch name at the end. I opted for the branch name approach: https://cdn.jsdelivr.net/gh/xheldon/x_blog-static@master.
Adding Gitalk
Gitalk is a comment tool specifically designed for GitHub Pages. It only requires importing CSS and JS files, then applying for a token and configuring it. Each time a new article is published, the author needs to open the page once, which automatically creates an issue in the corresponding GitHub Pages repository with the article’s title as the issue name. Subsequent comments on the article or the issue will then appear below the article, making it very convenient. Here, I also used jsDelivr’s CDN service for the CSS and JS files, which is officially supported.
Modifying Page Layout
First, I felt that while browsing articles, the large blank space on the left displaying the avatar and navigation was a waste of screen real estate. However, on the homepage, project pages, and project category pages, it was necessary to show the avatar, navigation, and search functionality. Therefore, I decided that on these pages, when the browser width exceeds 1080px, the navigation would appear on the left along with the search box. In other cases, the navigation bar would be displayed at the top.
Additionally, when viewing article pages on screens wider than 1080px, a table of contents (TOC) is displayed. This helps users quickly navigate to sections of interest and allows external sites to link directly to specific anchors, which also benefits SEO.
Adjusting Article Heading Structure
For SEO reasons, each page should have only one h1 tag, while multiple h2 or higher-level tags are acceptable. This helps search engines better understand the page structure.
Adding Project Pages
I added a dedicated personal projects page where all my projects are listed. Some link to external sites, while others are personal explanatory projects.
Epilogue
Optimization is an ongoing process… Once my wife finishes designing the layout for my personal blog, I might rebuild it using NodeJS for rendering. Stay tuned~
I often wish that when facing some key decisions in life, someone could tell me the best course of action so that I would not waste my precious time. Putting myself in others' shoes, I therefore write blogs often, hoping to record in this tiny corner of the vast Internet the once-in-a-lifetime experiences that matter to me, and to help those who seek help.