Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

nuxt-github-pages

mitre49Apache-2.01.5.0

Nuxt module to fix trailing slash issues for GitHub Pages deployments

nuxt, github-pages, static-site, deployment, trailing-slash

readme

nuxt-github-pages

npm version npm downloads License Nuxt CI

Nuxt module to fix trailing slash issues when deploying to GitHub Pages. This module ensures your static Nuxt site works correctly with GitHub Pages' file resolution behavior.

The Problem

When deploying a Nuxt 3 static site to GitHub Pages:

  • Visiting /docs/page works fine initially
  • But refreshing the page or directly visiting /docs/page/ returns a 404 error
  • This happens because Nuxt generates /docs/page.html but GitHub Pages expects /docs/page/index.html

The Solution

This module automatically creates duplicate HTML files during the build process. For every /path/index.html, it creates a /path.html file, ensuring URLs work with or without trailing slashes.

Features

  • 🚀 Zero configuration required
  • 🎯 Works with both GitHub Pages and Netlify
  • 📦 Lightweight with no runtime overhead
  • 🔧 Configurable output directories
  • 📝 TypeScript support
  • 🪵 Optional verbose logging
  • 🔍 SEO-friendly with automatic canonical URLs

Quick Setup

Installation

# npm
npm install nuxt-github-pages

# pnpm
pnpm add nuxt-github-pages

# yarn
yarn add nuxt-github-pages

# bun
bun add nuxt-github-pages

Configuration

Add nuxt-github-pages to the modules section of nuxt.config.ts:

export default defineNuxtConfig({
  modules: ['nuxt-github-pages']
})

That's it! The module will automatically run during nuxt generate.

Options

You can configure the module by adding options:

export default defineNuxtConfig({
  modules: ['nuxt-github-pages'],

  githubPages: {
    // Enable or disable the module (default: true)
    enabled: true,

    // Directories to check for output files (default: ['dist', '.output/public'])
    outputDirs: ['dist', '.output/public'],

    // Show verbose logging (default: true)
    verbose: true,

    // Add canonical URLs to prevent duplicate content SEO issues (default: true)
    canonicalUrls: true,

    // Use trailing slashes in canonical URLs (default: false)
    trailingSlash: false,

    // Create duplicate HTML files to avoid redirects (default: true)
    createDuplicates: true
  }
})

Option Details

enabled

  • Type: boolean
  • Default: true
  • Description: Enable or disable the module. Useful for conditional builds.

outputDirs

  • Type: string[]
  • Default: ['dist', '.output/public']
  • Description: Array of directories to check for generated files. The module will use the first directory that exists.

verbose

  • Type: boolean
  • Default: true
  • Description: Show detailed logging during the build process.

canonicalUrls

  • Type: boolean
  • Default: true
  • Description: Automatically inject canonical URLs into all HTML files to prevent duplicate content SEO issues. The canonical URL tells search engines which version of the page is preferred.

trailingSlash

  • Type: boolean
  • Default: false
  • Description: Whether to use trailing slashes in canonical URLs. When false, canonical URLs will be /path. When true, they will be /path/. Choose based on your site's URL structure preference.

createDuplicates

  • Type: boolean
  • Default: true
  • Description: Create duplicate HTML files to fix GitHub Pages routing. When true, creates both /path/index.html and /path.html. Set to false if you only need canonical URLs without file duplication (e.g., for other hosting platforms or if you handle redirects differently).

⚠️ Warning: Setting this to false will cause 404 errors on GitHub Pages for URLs without trailing slashes.

How It Works

  1. During the nuxt generate build process, this module hooks into the prerender:done event
  2. It scans the output directory for all index.html files
  3. For each index.html found at /path/to/page/index.html, it creates a duplicate at /path/to/page.html
  4. Optionally injects canonical URLs into all HTML files to prevent SEO duplicate content issues
  5. This ensures both /path/to/page and /path/to/page/ resolve correctly on GitHub Pages

Example

Given this generated structure:

dist/
├── index.html
├── about/
│   └── index.html
├── docs/
│   ├── index.html
│   └── getting-started/
│       └── index.html

The module creates these additional files:

dist/
├── about.html          ← Created
├── docs.html           ← Created
├── docs/
│   └── getting-started.html  ← Created

Deployment

After building with nuxt generate, deploy the output directory to GitHub Pages as usual:

# Build your site
pnpm run generate

# Deploy to GitHub Pages (example using gh-pages)
npx gh-pages -d dist

Compatibility

  • ✅ Nuxt 3.0+
  • ✅ GitHub Pages
  • ✅ Netlify
  • ✅ Any static hosting that serves .html files

Why Not Use Other Solutions?

Post-build Scripts

While you could use a custom post-build script, this module:

  • Integrates seamlessly with Nuxt's build process
  • Provides proper error handling and logging
  • Works with any package manager without extra configuration
  • Is reusable across projects

Netlify Redirects

Netlify-specific _redirects only work on Netlify. This module works everywhere.

.htaccess or nginx

These require server configuration access, which you don't have with GitHub Pages.

Development

Releasing New Versions

We use GitHub Actions for all production releases to ensure consistency and security.

# Using our convenience script
./scripts/release-prod.sh patch  # For bug fixes
./scripts/release-prod.sh minor  # For new features  
./scripts/release-prod.sh major  # For breaking changes

# Or directly with GitHub CLI
gh workflow run release.yml --field version_type=patch --field create_github_release=true

Option 2: Automated PR Release

Add a release label to your PR before merging:

  • release:patch - Bug fixes
  • release:minor - New features
  • release:major - Breaking changes

The release will trigger automatically when the PR is merged.

Local Development

# Test the release process without making changes
./scripts/release.sh patch --dry-run

# After a release, always sync your local repository
git pull origin main --tags

For more details, see CONTRIBUTING.md.

SEO Considerations

By default, this module injects canonical URLs to prevent duplicate content issues. Both /path and /path/ will point to the same canonical URL, telling search engines which version is preferred.

<!-- Both pages will have the same canonical URL -->
<!-- Default (trailingSlash: false): -->
<link rel="canonical" href="/about">

<!-- With trailingSlash: true: -->
<link rel="canonical" href="/about/">

You can customize this behavior:

export default defineNuxtConfig({
  modules: ['nuxt-github-pages'],
  githubPages: {
    // Disable canonical URLs entirely
    canonicalUrls: false,

    // Or use trailing slashes in canonical URLs
    trailingSlash: true  // Results in href="/path/"
  }
})

These recommendations are based on the excellent Trailing Slash Guide by Sébastien Lorber, which provides comprehensive testing and documentation of trailing slash behavior across all major hosting platforms. If you're dealing with trailing slash issues, his guide is an invaluable resource.

GitHub Pages

GitHub Pages redirects /about to /about/ on refresh, indicating it prefers trailing slashes:

export default defineNuxtConfig({
  modules: ['nuxt-github-pages'],
  githubPages: {
    canonicalUrls: true,
    trailingSlash: true  // URLs like /about/ (with trailing slash)
  }
})

Netlify

Netlify's "Pretty URLs" feature (enabled by default) prefers trailing slashes for directory-based content:

export default defineNuxtConfig({
  modules: ['nuxt-github-pages'],
  githubPages: {
    canonicalUrls: true,
    trailingSlash: true  // URLs like /about/ (with trailing slash)
  }
})

Cloudflare Pages

Cloudflare Pages automatically adds trailing slashes and creates redirects:

export default defineNuxtConfig({
  modules: ['nuxt-github-pages'],
  githubPages: {
    canonicalUrls: true,
    trailingSlash: true  // URLs like /about/ (with trailing slash)
  }
})

Vercel

Vercel is configurable but defaults to no trailing slashes:

export default defineNuxtConfig({
  modules: ['nuxt-github-pages'],
  githubPages: {
    canonicalUrls: true,
    trailingSlash: false  // URLs like /about (no trailing slash)
  }
})

Disable Canonical URLs

If you're managing canonical URLs through other means (like useHead or SEO modules):

export default defineNuxtConfig({
  modules: ['nuxt-github-pages'],
  githubPages: {
    canonicalUrls: false  // Don't inject any canonical URLs
  }
})

Troubleshooting

Module doesn't seem to run

  1. Ensure you're using nuxt generate (not nuxt build)
  2. Check that your nitro.preset is set to 'static' (this is the default for nuxt generate)
  3. Look for log messages in your build output

Files aren't being created

  1. Verify your output directory matches one in outputDirs
  2. Enable verbose: true to see detailed logging
  3. Ensure your build is completing successfully

Custom output directory

If you use a custom output directory, add it to the configuration:

export default defineNuxtConfig({
  modules: ['nuxt-github-pages'],
  githubPages: {
    outputDirs: ['my-custom-dist', 'dist', '.output/public']
  }
})

Testing

# Run tests
pnpm run test

# Run tests in watch mode
pnpm run test:watch

See test/README.md for detailed information about the testing approach and patterns used in this module.

See test/TEST_MATRIX.md for current test coverage.

Contributing

We welcome contributions! Please see our Contributing Guide for details on:

  • Development setup and workflow
  • Automated quality checks (git hooks)
  • Testing guidelines
  • Pull request process
  • Coding standards

Quick Start for Contributors

# Setup
pnpm install
pnpm run dev:prepare

# Before committing
pnpm run test:all

CI/CD Process

This project uses GitHub Actions for continuous integration and deployment:

  • CI: Runs on every push and PR (linting, type checking, tests, security)
  • Release: Manual workflow for creating releases (version bump, npm publish, GitHub release)
  • Auto Release: PRs with release:patch, release:minor, or release:major labels trigger automatic releases when merged

See .github/SETUP.md for workflow configuration details.

Development

# Install dependencies
pnpm install

# Generate type stubs
pnpm run dev:prepare

# Develop with the playground
pnpm run dev

# Build the playground
pnpm run dev:build

# Run ESLint
pnpm run lint

# Run type checking
pnpm run test:types

# Clean build artifacts
pnpm run clean

# Run ALL checks (recommended before committing)
pnpm run test:all

# Release new version
pnpm run release

Development Workflow

  1. Before starting work: Run pnpm install and pnpm run dev:prepare
  2. During development: Use pnpm run dev to test changes in the playground
  3. Before committing: Run pnpm run test:all to ensure everything passes
  4. After testing: Run pnpm run clean to remove build artifacts

Automated Quality Checks

This project uses git hooks to maintain code quality:

  • Pre-commit hook: Automatically runs ESLint on staged files and fixes issues
  • Automatic formatting: Code style issues are fixed automatically on commit
  • Files are linted and formatted using the Nuxt ESLint configuration

Development Scripts

  • pnpm run test:all - Complete test suite (install → prepare → test → lint → type check → clean)
  • pnpm run clean - Remove all build artifacts (preserves node_modules)
  • ./scripts/pre-release.sh - Run all checks and build before releasing

Development Tips

  • The playground directory is for testing during development
  • Test fixtures are automatically cleaned during test runs
  • Build artifacts (.nuxt, .output, dist) are git-ignored
  • Always run pnpm run test:all before pushing changes

License

Apache-2.0 License

See NOTICE.md for additional copyright and license information.

Security

For security issues, please see our Security Policy.

Code of Conduct

This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code.

Credits

This module was created to solve the long-standing trailing slash issue when deploying Nuxt sites to GitHub Pages.

Created by MITRE for the open source community.

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

🚀 Enhancements

  • Add createDuplicates option to control HTML file duplication
  • Add Dependabot auto-merge workflow for patch/minor updates
  • Add release-prod.sh script for streamlined GitHub Actions releases
  • Add --dry-run flag to release script for testing
  • Add npm version sync checking to prevent conflicts
  • Establish GitHub Actions as primary release workflow

🩹 Fixes

  • Make release workflow reusable with workflow_call trigger
  • Handle both workflow_dispatch and workflow_call inputs in release workflow

📖 Documentation

  • Clarify release workflows in README and CONTRIBUTING
  • Document auto-release workflow requirements
  • Add dependency management section to CONTRIBUTING
  • Improve auto-release safety by requiring explicit labels

🏡 Chore

  • deps: Bump softprops/action-gh-release from 1 to 2
  • deps-dev: Bump vitest from 3.1.4 to 3.2.0

⚡ Performance

  • Optimize CI with 3-minute timeouts for build-heavy tests

🔧 Development

  • Require --local flag for direct releases via release.sh
  • Add comprehensive workflow documentation in .github/SETUP.md

v1.1.1

compare changes

[1.1.0] - 2025-02-03

Added

  • Canonical URL injection feature to prevent SEO duplicate content issues (#15462)
    • New canonicalUrls option (default: true) - automatically adds canonical URLs to both original and duplicate HTML files
    • New trailingSlash option (default: false) - controls whether canonical URLs use trailing slashes
    • Both original and duplicate files receive the same canonical URL to prevent duplicate content penalties
  • Comprehensive test suite improvements
    • All integration tests rewritten to use real build environments
    • Added canonical URL feature tests
    • Added test isolation with temporary fixtures
    • Created test/README.md with testing patterns and guidelines

Changed

  • Default values updated:
    • verbose now defaults to true (was false)
    • canonicalUrls defaults to true (new option)
  • Improved error handling and logging throughout the module

Fixed

  • All previously skipped tests are now passing
  • Test infrastructure issues that prevented proper module testing

Documentation

  • Added SEO considerations section to README
  • Added platform-specific configuration examples
  • Created comprehensive testing documentation
  • Added references to slorber's trailing-slash-guide

Development

  • Added comprehensive developer tooling:
    • pnpm run clean script to remove build artifacts
    • pnpm run test:all script for complete test suite
    • ./scripts/pre-release.sh for pre-release checklist
  • Implemented git hooks with Husky and lint-staged:
    • Pre-commit hook automatically runs ESLint on staged files
    • Code formatting issues are fixed automatically
  • Enhanced .gitignore to exclude AI assistant files (CLAUDE.md, recovery-prompt.md)
  • Improved contributing guidelines with automated quality checks
  • Added development workflow documentation
  • Implemented security tooling:
    • Added pnpm audit to test scripts
    • Created SECURITY.md policy
    • Added Dependabot configuration for automatic updates
    • Integrated security checks into CI/CD workflow

[1.0.0] - 2025-01-28

Added

  • Initial release of nuxt-github-pages module
  • Automatic creation of duplicate HTML files for GitHub Pages compatibility
  • Configurable output directories
  • Verbose logging option
  • TypeScript support
  • Zero-configuration setup for most use cases

Features

  • Creates /path.html for every /path/index.html during build
  • Handles nested directory structures
  • Skips special directories (starting with _ or .)
  • Preserves root index.html without duplication
  • Works with both dist and .output/public directories

Production Ready

  • Successfully deployed and tested on production sites including https://act.mitre.org/
  • Comprehensive error handling
  • Lightweight with no runtime overhead (5.67 kB total)