Add Broken Link Checking to Astro Framework with Astro Link Validator

· Travis Rodgers  · 6 min read

superhero flexing muscles representing broken links

When building static sites with the Astro framework, broken links are a common issue that can slip into production. Files get moved, URLs change during refactoring, or assets are reorganized. While you could manually click through every page, there’s a better way.

I recently built Astro Link Validator, an Astro integration that automatically validates links during the build process. Let’s look at how it works and why it can be valuable for your Astro projects.

Broken links are particularly common in Astro sites due to how static site generation works:

  • File Structure Changes – Moving pages in /src/pages changes generated URLs, but internal links may not get updated
  • Asset Path Issues – Images or styles referenced from different directory levels can break when output paths differ
  • Build-Time Transformations – Astro’s build can change how URLs resolve (trailing slashes, base paths, etc.)
  • Environment Differences – Links that work locally may break in production due to base URLs or deployment configs

Manually testing every link doesn’t scale, especially for content-heavy sites. Automation is the safer option.

Astro Link Validator hooks into the Astro build process and automatically validates all links in your generated HTML files. Here’s what it does:

  1. Build Hook Integration – Runs after the site is built using astro:build:done
  2. HTML Parsing – Scans all .html files in the dist/ output
  3. Link Extraction – Finds href, src, and srcset attributes
  4. Validation – Confirms internal links exist, optionally checks external URLs with HTTP requests
  5. Reporting – Outputs a detailed report showing which links are broken and why

Why This Approach Works

  • Build-Time Validation: Issues are caught before deployment
  • Zero Configuration: Works with sensible defaults out of the box
  • Comprehensive: Handles pages, assets, images, and srcset URLs
  • Security-Aware: Includes path traversal protection
  • Performance Optimized: Uses concurrent checks for speed

Installation

Since the integration isn’t published to npm yet, install directly from GitHub:

npm install github:rodgtr1/astro-link-validator

Important Notes:

  • No Build Required: Prebuilt JavaScript files included — works immediately after installation
  • TypeScript Support: Full type definitions included, but TypeScript is not required
  • Updates: To update to a newer version, uninstall and reinstall:
npm uninstall astro-link-validator
npm install github:rodgtr1/astro-link-validator

Configuration

Add it to your Astro config:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import linkValidator from 'astro-link-validator';

export default defineConfig({
  integrations: [
    linkValidator()
  ],
});

Usage

Run your normal build:

npm run build

If broken links exist, the build fails and outputs details.

Astro Link Validator uses Cheerio (server-side jQuery) to parse HTML and extract links.

Checked Elements

Navigation links:

<a href="/about">About</a>
<a href="./contact.html">Contact</a>
<a href="#section-1">Jump</a>

Assets:

<img src="/images/hero.jpg" />
<link rel="stylesheet" href="/styles/main.css" />
<script src="/js/app.js"></script>

Responsive images:

<img src="small.jpg" srcset="medium.jpg 768w, large.jpg 1024w" />

External links (optional):

<a href="https://github.com/user/repo">GitHub</a> <link href="https://fonts.googleapis.com/css" />

Validation Rules

  • Internal Links: Checked against filesystem with fallbacks (.html, index.html, relative resolution)
  • External Links: HTTP HEAD requests (configurable, disabled by default for speed)

Skipped URLs

  • mailto:
  • tel:
  • javascript:
  • data: / blob:

External link validation requires network requests and can significantly slow build times. Here’s the recommended approach:

For Regular Development

linkValidator({
  checkExternal: false, // Skip external links for fast builds
});

For Periodic Validation

Run external link checking 1-2 times per month (or more frequently for sites with many external references):

// Run this configuration locally on a schedule
linkValidator({
  checkExternal: true,
  externalTimeout: 8000, // Allow more time for external requests
  verbose: true, // See progress during long checks
});

Build Time Impact

  • Without external checking: Builds complete in seconds
  • With external checking: Can add minutes to build time depending on number of external links
  • Recommendation: External checking frequency depends on your publishing schedule and external link volatility

Example Configurations

Development (fast feedback):

linkValidator({
  checkExternal: false,
  failOnBrokenLinks: false,
  verbose: true,
});

Production (strict checks):

linkValidator({
  checkExternal: true,
  failOnBrokenLinks: true,
  exclude: ['/admin/*', '/drafts/*'],
});

With redirects (Netlify/Cloudflare Pages):

linkValidator({
  redirectsFile: '_redirects', // Both Netlify and Cloudflare Pages use this format
});

Output Example

When everything is fine:

🔗 Validating links...
 Checked 47 links across 12 files
🎉 No broken links found!

With issues:

 Found 3 broken links:

📄 blog/astro-tips.html:
  🔗 /missing-page File not found
  📦 /images/missing.jpg Asset not found

📄 about/index.html:
  🔗 https://dead-link.example.com 404 Not Found

Configuration Reference

linkValidator({
  checkExternal: true,
  externalTimeout: 10000,
  failOnBrokenLinks: true,
  verbose: false,
  include: ['**/*.html'],
  exclude: ['/admin/*', '/api/*', '*.pdf'],
  base: 'https://mysite.com',
  redirectsFile: '_redirects',
});

TypeScript Support

import linkValidator, { type LinkValidatorOptions } from 'astro-link-validator';

const config: LinkValidatorOptions = {
  checkExternal: false,
  verbose: true,
};

export default defineConfig({
  integrations: [linkValidator(config)],
});

Programmatic Usage

import { checkLinks } from 'astro-link-validator';

const result = await checkLinks('./dist', {
  checkExternal: true,
  verbose: true,
});

console.log(`Broken links: ${result.brokenLinks.length}`);

GitHub Distribution Approach

Why Install from GitHub?

Currently, this integration is distributed directly from GitHub rather than npm. This approach has some implications:

Committed Build Files: The repository includes a dist/ folder with pre-built JavaScript and TypeScript definitions. This is uncommon for npm packages but necessary for GitHub installations to work without requiring users to build the package themselves.

Update Process: When updating to newer versions, npm may not always properly update GitHub-installed packages. If you encounter issues after an update, try:

npm uninstall astro-link-validator
npm install github:rodgtr1/astro-link-validator

Redirect Support Examples

The validator supports the _redirects format used by both Netlify and Cloudflare Pages:

Netlify/Cloudflare Pages _redirects file:

/old-page /new-page 301
/blog/:slug /posts/:slug 301
/api/* https://api.example.com/v1/* 200
/docs/* /documentation/:splat 301

This prevents false positives when links are redirected rather than actually broken.

Future Plans: Eventually, this integration will be published to npm, which will provide a cleaner update experience and remove the need for committed build files.

Security & Performance

  • 🔒 Path Traversal Protection
  • 🔒 URL Validation
  • 🚀 Concurrent checks
  • 🚀 Skip duplicate URLs
  • 🚀 Configurable timeouts

Limitations and Future Improvements

Current Limitations

  • External Link Checking: Disabled by default due to significant performance impact. Each external URL requires a network request, which can add substantial time to builds. For sites with many external links, enabling this can increase build times from seconds to minutes.
  • Redirect Support: Currently supports Netlify/Cloudflare Pages _redirects format only (Vercel JSON format not yet supported)
  • Hash Links: Basic validation only - doesn’t verify that target elements exist
  • JavaScript-Generated Links: Can’t validate links created dynamically by JavaScript
  • Medium to large Astro sites where manual link checking isn’t practical
  • Content-heavy sites with frequent updates and reorganization
  • CI/CD environments where you want to catch broken links before deployment
  • Team environments where multiple people contribute content

Summary

The astro-link-validator integration provides automated link validation for Astro sites by:

  1. Hooking into the build process using Astro’s integration API
  2. Parsing generated HTML files to extract all links
  3. Validating internal links against the filesystem with intelligent fallbacks
  4. Optionally checking external links via HTTP requests
  5. Reporting detailed errors that help you locate and fix broken links

Key Benefits:

  • Zero configuration required for basic usage
  • Build-time validation catches issues before deployment
  • Security-hardened with path traversal protection
  • Performance optimized with concurrent processing
  • Full TypeScript support (but TypeScript not required)

Resources

The integration is released under the MIT license and is free to use in both personal and commercial projects.

This page may contain affiliate links. Please see my affiliate disclaimer for more info.

Related Posts

View All Posts »