Web Development

Master GitHub Pages: Clean URLs from Subdirectories 2025

Tired of .html extensions or trailing slashes on your GitHub Pages site? Learn the definitive 2025 guide to creating clean, professional URLs from subdirectories.

A

Alex Carter

A front-end developer and static site enthusiast passionate about web performance.

7 min read11 views

You’ve done it. You’ve built a sleek, fast, and free website using GitHub Pages. Your portfolio is polished, your blog posts are insightful, but there’s one small thing that keeps nagging at you: the URLs. Seeing your-awesome-site.github.io/about.html or your-awesome-site.github.io/contact/ just doesn’t feel as professional as the clean, extensionless URLs you see on major websites. It’s a small detail, but in web design, the details make all the difference.

If you've ever felt that twinge of URL-envy, you're in the right place. For years, developers have wrestled with GitHub Pages to produce clean, user-friendly URLs. The good news? As of 2025, the methods are more refined and powerful than ever. We're moving beyond simple tricks to robust, automated workflows. This guide will walk you through the definitive methods, from the classic approach to a modern, automated solution using GitHub Actions, ensuring your site's URLs are as clean as your code.

Why Do Clean URLs Even Matter?

Before we dive into the “how,” let’s quickly touch on the “why.” It’s more than just aesthetics:

  • User Experience (UX): Clean URLs like /services are easier for users to read, remember, and type than /services.html. They imply a well-organized site structure.
  • SEO Benefits: While modern search engines are smart enough to handle .html extensions, a clean, canonical URL (e.g., /about instead of having both /about/ and /about.html as possibilities) is considered a best practice. It helps consolidate link equity and prevents duplicate content confusion.
  • Future-Proofing: What if you move your site from static HTML to a CMS like WordPress or Ghost? Your URL structure can remain the same (/about), providing a seamless transition. If you used .html, you'd be forced to set up redirects to preserve your SEO rankings.

Understanding the GitHub Pages Default

Out of the box, GitHub Pages serves files exactly as they are in your repository. If you have a file named about.html in the root of your repository, it will be accessible at your-username.github.io/about.html. Simple and predictable, but not what we're aiming for.

The core principle behind clean URLs on most web servers, including those used by GitHub Pages, is the concept of a directory index. When a server receives a request for a directory (e.g., /about/), it looks for a default file to serve, which is almost always index.html. All the methods below leverage this behavior in one way or another.

Method 1: The Classic “index.html in a Folder” Trick

This is the most fundamental and tool-free way to achieve cleaner URLs on GitHub Pages. It requires a manual change to your file structure, but it’s incredibly reliable.

How It Works

Instead of creating a file called about.html, you create a folder called about. Inside that folder, you place your content in a file named index.html.

Your repository structure would look like this:

Advertisement
my-gh-pages-repo/
├── index.html         (Your homepage)
├── about/
│   └── index.html     (Your "About" page content)
├── contact/
│   └── index.html   (Your "Contact" page content)
└── css/
    └── style.css

When a user navigates to your-site.com/about/, the server automatically serves the index.html file from within the about directory. Most modern browsers will even hide the trailing slash, displaying the URL as your-site.com/about in the address bar.

Pros and Cons

  • Pros: No special tools or configuration needed. It works universally and is easy to understand.
  • Cons: It can make your file structure feel cluttered. Manually creating a folder for every single page can become tedious for larger sites. The canonical URL technically includes a trailing slash (/about/).

Method 2: Automated Permalinks with Jekyll

If you're using the default GitHub Pages build process, you're already using Jekyll, its built-in static site generator (SSG). Jekyll can automate Method 1 for you with a simple configuration called permalinks.

How It Works

With Jekyll, you work with Markdown files (e.g., about.md) and add instructions in a section at the top called "front matter." The permalink setting tells Jekyll what the final URL should be. Jekyll then generates the necessary folder and index.html file for you during its build process.

Your source file, about.md, would look like this:

---
layout: default
title: About Us
permalink: /about/
---

# About Our Company

This is the content of our about page, written in Markdown...

When you push this file to GitHub, Jekyll processes it and automatically creates the /about/index.html structure in your deployed site. You get the benefit of a clean file structure during development and clean URLs on the live site.

Pro Tip: You can set a default permalink style for all your pages in your site's _config.yml file by adding the line: permalink: /:title/. This tells Jekyll to use the page's title to create the URL structure for every page automatically.

Pros and Cons

  • Pros: Keeps your source repository clean and flat. Automates the folder creation process. Highly configurable.
  • Cons: Requires you to use Jekyll's conventions. If you just want to write plain HTML, this adds a layer of abstraction you might not need.

Method 3: The Ultimate Fix with GitHub Actions (Advanced)

This is the powerhouse method for 2025. It gives you complete control over your build and deployment process, regardless of the tools you use. Whether you're writing vanilla HTML, using a modern SSG like Eleventy or Hugo, or have a custom build script, GitHub Actions can automate the process of creating a clean URL structure.

How It Works

The idea is to use a GitHub Actions workflow to run a script that transforms your source files into the required directory/index.html structure before deploying to the gh-pages branch. This separates your source code from the compiled, deployment-ready code.

Let's imagine you have a simple project with HTML files in a src directory:

my-vanilla-html-site/
├── src/
│   ├── index.html
│   ├── about.html
│   └── contact.html
└── .github/
    └── workflows/
        └── deploy.yml

Your deploy.yml workflow file would perform the magic. This script finds every HTML file (except index.html), creates a directory with its name, and moves it to become the index.html inside that new directory.

# .github/workflows/deploy.yml
name: Build and Deploy to GitHub Pages

on:
  push:
    branches:
      - main # Or your default branch

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Clean URL Structure
        run: |
          # Create a directory for the output
          mkdir -p dist
          # Find all html files in src, but exclude index.html and 404.html
          find src -type f -name "*.html" ! -name "index.html" ! -name "404.html" | while read file; do
            # Get filename without extension (e.g., "about" from "src/about.html")
            dirname=$(basename "$file" .html)
            # Create the directory structure in dist
            mkdir -p "dist/$dirname"
            # Move and rename the file
            mv "$file" "dist/$dirname/index.html"
          done
          # Copy the main index.html and any other root files
          cp src/index.html dist/
          # If you have a 404 page, copy it too
          [ -f src/404.html ] && cp src/404.html dist/
          # Copy other assets like CSS or JS
          [ -d src/css ] && cp -r src/css dist/

      - name: Deploy to gh-pages branch
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./dist

Pros and Cons

  • Pros: Infinitely flexible. Works with any static site generator or even plain HTML. Keeps your source repository completely clean. Full automation on every push.
  • Cons: Requires understanding of GitHub Actions and basic shell scripting. It's the most complex setup of the three.

Head-to-Head: Comparing the Methods

Let's see how the methods stack up against each other in a quick-reference table.

Method Ease of Use Flexibility Resulting URL Best For
Folder Trick Easy Low (Manual) /page/ Very small sites with 2-3 pages.
Jekyll Permalinks Medium Medium (Jekyll-only) /page/ Blogs and sites built with Jekyll.
GitHub Actions Hard (Advanced) High (Tool-agnostic) /page/ Any project, especially those using non-Jekyll SSGs or custom builds.

Conclusion: Choosing Your Path to Cleanliness

As we've seen, achieving professional, clean URLs on GitHub Pages is entirely possible. The underlying mechanism remains the same—leveraging the server's ability to serve index.html from a directory—but the way you generate that structure has evolved.

  • For a quick and simple site, the manual folder trick is perfectly fine.
  • If you're already embracing the GitHub Pages ecosystem, mastering Jekyll's permalinks is the most efficient path.
  • And for the modern developer seeking ultimate control and automation, GitHub Actions is the undisputed champion, allowing you to build your site exactly how you want while still producing the clean, optimized output GitHub Pages needs.

Don't let clunky URLs undermine your beautifully crafted site. Pick the method that best fits your workflow, and give your project the polished, professional web presence it deserves.

Tags

You May Also Like