Made Mistakes

Jekyll’s site.url and baseurl

Learn what Jekyll’s site url and baseurl variables are and how to properly set them with relative_url and absolute_url filters to avoid broken links and 404 errors.

Jekyll’s site url and baseurl variables cause a lot of confusion for users. I see it all the time in the Jekyll Talk forum, Stackoverflow, or as bug reports in my themes.

My Jekyll site works locally but when I push it up to GitHub Pages it is broken with no styling and looks like this. Help!"

Jekyll Minima site with missing CSS

Improper use of Jekyll’s baseurl can break links to CSS, posts, and more as seen above.

What are url and baseurl?

So what exactly are the url and baseurl variables? To start, both are site-wide variables set in the _config.yml file and affect how Jekyll builds URLs. I like to describe them like this:

Site variableDescription
urlA site’s full URL including protocol, domain, and port (if applicable).
baseurlName of sub-directory the site is served from e.g., /blog.

illustration describing site url and baseurl permalink structure

How to use url and baseurl

A couple of important facts to keep in mind when using both:

  1. Leave off trailing forward slashes when setting url

    ✅ Do this❌ Don’t do this
    website: https://mademistakes.comwebsite: https://mademistakes.com/
  2. baseurl is not needed for most sites and can be omitted.

  3. baseurl is only necessary when hosting your site in a sub-directory. Project sites hosted on GitHub Pages are the common use-case of this variable.

Jekyll URL troubleshooting

If we go back to the example above with the broken CSS link and inspect the HTML’s source. You will often find that the link to the CSS file is incorrect for any number of reasons:

  1. Double forward slashes e.g., //minima/assets/style.css
  2. Missing base URL due to not using relative_url filter, absolute_url filter, or {{ site.baseurl }} in theme files.

Firefox web inspector showing HTML source and broken stylesheet reference

When the path to the stylesheet is wrong the browser can’t load it. No stylesheet == plain looking website with no styles and looks broken.

Jekyll development and site.url

One other question I see asked over and over again from Jekyll users is:

Why are my links broken? They all start with http://localhost:4000 or http://127.0.0.1:4000.

Screenshot of GitHub Pages default 404 file not found page

This happens because in older versions of Jekyll (v3.3 through 4.1) it would reset site.url to localhost:4000 when JEKYLL_ENV=development (the default environment value), overriding whatever is set in the _config.yml. In newer versions of Jekyll (v4.2 and up) this reset of site.url no longer happens.

In older versions of Jekyll, site.url is also reset to http://localhost:4000 when spinning up a development server with the jekyll serve command.

site.url values

Jekyll versionEnvironmentCLI commandsite.url value
4.2 (✨latest)developmentservevalue set in _config.yml
3.3developmentservereset to http://localhost:4000
4.2 (✨latest)developmentbuildvalue set in _config.yml
3.3developmentbuildvalue set in _config.yml
4.2 (✨latest)productionservevalue set in _config.yml
3.3productionservevalue set in _config.yml
4.2 (✨latest)productionbuildvalue set in _config.yml
3.3productionbuildvalue set in _config.yml

Under normal circumstances these two conditions won’t cause any headaches, especially when hosting on GitHub Pages as the environment and url are set automatically.

The issue surfaces when building locally, using an older versions of Jekyll, and then forgetting to set JEKYLL_ENV to production. Or pushing up files from a _site directory after running jekyll serve instead of jekyll build.

Examples

Below you will find examples of how to write different types of links that require site.url or site.baseurl values to work properly. Links marked as ✅ have valid URLs while those marked as ❌ are broken and will trigger a 404 file not found error.

Each of the examples assume:

  1. Jekyll v4.2 or greater is installed.

  2. The site is hosted on GitHub Pages in a sub-directory named blog.

  3. Posts follow standard naming conventions of YYYY-MM-DD-filename.md inside of a _posts directory.

  4. Image assets are in a images directory inside of the root.

  5. Have the following _config.yml settings:

    website: https://mmistakes.github.io
    basewebsite: /blog
    permalink: date

How to link to pages, posts, and images in Markdown.

Page in root directory

[about page](/about.html)
HTML output-
<a href="/about.html">about page</a>
URL is missing the base URL of /blog from the path.

Page in sub-directory

[about page](/blog/about.html)
[about page]({{ site.baseurl }}/about.html)
[about page]({{ 'about.html' | relative_url }})
HTML output-
<a href="/blog/about.html">about page</a>

Page in date sub-directories

[Welcome to Jekyll post](/blog/2021/06/29/welcome-to-jekyll.html)
[Welcome to Jekyll post]({% post_url 2021-06-29-welcome-to-jekyll %})
[Welcome to Jekyll post]({% link _posts/2021-06-29-welcome-to-jekyll.md %})
HTML output-
<a href="/blog/2021/06/29/welcome-to-jekyll.html">Welcome to Jekyll post</a>

Image in root directory

[cheese pizza](pizza.jpg)
HTML output-
<img src="pizza.jpg" alt="cheese pizza">
URL is missing the base URL of /blog and sub-directory /images from the path.

Image in sub-directory

[cheese pizza](images/pizza.jpg)
HTML output-
<img src="images/pizza.jpg" alt="cheese pizza">
URL is document-relative when it should be root-relative and is missing the base URL of /blog from the path.
[cheese pizza](/images/pizza.jpg)
HTML output-
<img src="/images/pizza.jpg" alt="cheese pizza">
URL is missing the base URL of /blog from the path.
[cheese pizza](/blog/images/pizza.jpg)
[cheese pizza]({{ '/images/pizza.jpg' | relative_url }})
[cheese pizza]({{ site.baseurl }}/images/pizza.jpg)
HTML output-
<img src="/blog/images/pizza.jpg" alt="cheese pizza">

How to link resources with root-relative URLs, like stylesheets, favicons, and JavaScript.

<link rel="stylesheet" href="/assets/css/style.css">
HTML output-
<link rel="stylesheet" href="/assets/css/style.css">
URL is missing the base URL of /blog from the path.
<link rel="stylesheet" href="/blog/assets/css/style.css">
<link rel="stylesheet" href="{{ site.baseurl }}/assets/css/style.css">
<link rel="stylesheet" href="{{ '/assets/css/style.css' | relative_url }}">
HTML output-
<link rel="stylesheet" href="/blog/assets/css/style.css">

How to link to resources that require the full URL, like a canonical URL in the page’s <head>.

<link rel="canonical" href="{{ page.url }}">
HTML output-
<link rel="canonical" href="/2021/06/29/welcome-to-jekyll.html">
URL is missing the site URL and base URL of /blog from the path.
<link rel="canonical" href="{{ page.url | absolute_url }}">
<link rel="canonical" href="{{ page.url | prepend: site.baseurl | prepend: site.url }}">
<link rel="canonical" href="{{ site.url }}{{ site.baseurl }}{{ page.url }}">
HTML output-
<link rel="canonical" href="https://mmistakes.github.io/blog/2021/06/29/welcome-to-jekyll.html">

As you can see from above examples, Jekyll is flexible and allows for multiple ways of creating links. Personally I tend to lean on the relative_url and absolute_url filters as they require less typing and have some “smarts” to them.

The best option is the one that works for you!

4 mentions

  1. Kat

    This post saved my life.

    THANK YOU, from a cheap musician building her own portfolio with limited experience. The urls were driving me crazy and now they aren’t. I love it when everything just works.

  2. Dalibor Pavlík

    Good article. But still, there is something that I am missing. This is an example.

    I have this baserul: /blog This is my path to css: <link rel="stylesheet" href="{{ site.baseurl }}/css/pages-style.css">

    I develop locally and run jekyll serve. When I inspect the page, this what I see in the head of the page.

    <link rel="stylesheet" href="/blog/css/pages-style.css">

    But why? Why does jekyll inject the baseurl locally. It does not need to be there locally. It need to be there when I deploy the page on github. Correct? It just does not make sense to me.

    Anyway, thank you for the article. It summarizes the issues well. But still, I am puzzled. I will probably be adding the site.baseurl to config file just before deployment.

  3. Michael Rose

    It adds the baseurl because it is helpful to know that your file paths will be correct or not. Jekyll is emulating how the file paths would need to be when your site is uploaded to /blog.

    If it stripped /blog from the URLs then you wouldn’t know if you had bad paths or links prior to pushing up to GitHub to host on Pages.

    If you really don’t want to see /blog in your paths when running jekyll serve locally, then you can completely remove basewebsite: /blog from your _config.yml file. GitHub Pages automatically adds that value based on the repo and type of site (user or project site).

    For example if you have a repo named blog it would be a project site and GitHub would automatically add basewebsite: /blog when building your Jekyll site.

  4. Irina Ivanova

    You saved my day! This piece of code is the answer for the problem I tried to solve for couple of days: {{ page.url | prepend: site.baseurl | prepend: site.url }}

    I just leave it here, that it also works with post.url, not only page.url.

Related