astro-link-validator
v1.2.2
Published
An Astro integration that validates links during build time
Maintainers
Readme
🔗 Astro Link Validator

Automatically validates links during your Astro build process with security-hardened validation and high-performance concurrent processing.
🚀 Quick Start
Step 1: Install from npm
npm install astro-link-validatorStep 2: Add to your Astro config
// astro.config.mjs
import { defineConfig } from 'astro/config';
import linkValidator from 'astro-link-validator';
export default defineConfig({
integrations: [
linkValidator() // ← Add this line
],
});Step 3: Build and check
npm run build # Link checking runs automatically!💡 Pro tip: Works with any Astro project - no additional setup required!
🔧 Requirements
- Node.js 18+
- Astro 4.0+, 5.0+, 6.0+, or 7.0+
TypeScript Support: Full TypeScript definitions included, but TypeScript is not required to use this integration. Works perfectly with JavaScript-only projects.
✨ Key Features
✅ Path Traversal Protection - Secure validation prevents malicious link attacks
✅ High Performance - Concurrent processing up to 10x faster than sequential
✅ Zero Configuration - Works out of the box with sensible defaults
✅ Beautiful Output - Color-coded error reports with exact locations
✅ CI/CD Ready - Perfect for GitHub Actions and automated builds
✅ TypeScript Support - Full type definitions included
✅ Comprehensive Checking - Internal links, assets, responsive images, and more
✅ Verify Installation
After installing and configuring, verify it's working:
- Check your
package.json- You should seeastro-link-validatorlisted in dependencies - Run a build -
npm run buildshould show "🔗 Checking links..." in the output - Test with a broken link - Add
<a href="/nonexistent">Test</a>to any page and rebuild
You should see something like:
❌ Found 1 broken links:
📄 index.html:
🔗 /nonexistent
File not found: nonexistent📚 Example Output
When you run npm run build, you'll see:
🔗 Checking links...
✅ Checked 23 links across 4 files
🎉 No broken links found!Or if there are issues:
❌ Found 2 broken links:
📄 about/index.html:
🔗 /missing-page
File not found: missing-page
Text: "Click here"
📦 /images/logo.png
File not found: images/logo.png
Build failed: Found 2 broken links⚙️ Configuration Options
| Option | Type | Default | Description | When to Use |
|--------|------|---------|-------------|-------------|
| checkExternal | boolean | false | Enable checking of external HTTP(S) links | Production builds, comprehensive testing |
| failOnBrokenLinks | boolean | true | Whether to fail the build when broken links are found | CI/CD pipelines, production deploys |
| exclude | string[] | [] | Link patterns to skip, matched against the link's href | Skip admin areas, APIs, external CDNs |
| include | string[] | ['**/*.html'] | File patterns to check, matched against paths relative to the build directory | Custom build outputs, specific directories |
| externalTimeout | number | 5000 | Timeout in milliseconds for external link requests | Slow networks, comprehensive external checking |
| verbose | boolean | false | Show detailed logging information | Debugging, development, progress monitoring |
| redirectsFile | string | undefined | Path to redirects file (e.g., '_redirects', 'vercel.json') | Netlify/Cloudflare/Vercel deployments with redirects |
| base | string | Astro's base config | Base path prefix stripped from root-relative links | Only when calling checkLinks directly — the integration reads it from your Astro config |
Pattern Matching
Both exclude and include accept wildcard patterns where * matches any run of characters, including /:
| Pattern | Matches |
|---------|---------|
| /admin/* | /admin/users, /admin/settings/advanced (but not /admin itself) |
| *.pdf | /docs/manual.pdf, /whitepaper.pdf |
| https://analytics.google.com/* | Any URL on that host |
An exclude pattern with no * is matched as a plain substring, so exclude: ['/drafts'] skips every link whose href contains /drafts.
include patterns are matched against each file's path relative to the build directory, using / as the separator on every platform. A leading **/ also matches the root, so the default **/*.html covers both index.html and blog/post/index.html. An include pattern with no * must match the path exactly (e.g. index.html).
️ Usage Examples
Development vs Production
// Development: Fast & forgiving
linkValidator({
checkExternal: false,
failOnBrokenLinks: false,
verbose: true
})
// Production: Comprehensive & strict
linkValidator({
checkExternal: true,
failOnBrokenLinks: true,
exclude: ['/admin/*', '*.pdf']
})Complete Configuration (All Options)
// astro.config.mjs - Showing ALL available options
import { defineConfig } from 'astro/config';
import linkChecker from 'astro-link-validator';
export default defineConfig({
integrations: [
linkValidator({
// External link checking
checkExternal: false, // Enable/disable external link checking
externalTimeout: 5000, // Timeout for external requests (ms)
// Build behavior
failOnBrokenLinks: true, // Fail build on broken links
verbose: false, // Show detailed progress
// File inclusion/exclusion
include: ['**/*.html'], // File patterns to check
exclude: [ // Link patterns to skip
'/admin/*', // Skip admin pages
'/api/*', // Skip API routes
'*.pdf', // Skip PDFs
'https://analytics.google.com/*' // Skip tracking
],
// Advanced options
redirectsFile: '_redirects' // Path to redirects file (Netlify/Cloudflare/Vercel)
})
],
});TypeScript Configuration
// astro.config.mts - With full TypeScript support
import { defineConfig } from 'astro/config';
import linkChecker, { type LinkValidatorOptions } from 'astro-link-validator';
const linkCheckerConfig: LinkValidatorOptions = {
checkExternal: false,
verbose: true,
exclude: ['/admin/*'],
};
export default defineConfig({
integrations: [
linkValidator(linkCheckerConfig)
],
});Sites Deployed to a Subpath (base)
If your astro.config.mjs sets base (e.g. for GitHub Pages), the integration picks it up automatically — no extra configuration needed:
export default defineConfig({
base: '/docs',
integrations: [linkValidator()]
});Astro prefixes generated URLs with the base (/docs/about/) while the output files stay at the root of dist/. The validator strips the prefix before resolving links to files. A root-relative link that omits the prefix (e.g. a hardcoded /about/) is reported as broken, because it would 404 on the deployed site. If such a link is intentional — say it points at another app on the same domain — add it to exclude.
Redirects Support
If your site uses redirects (Netlify, Vercel, Cloudflare Pages, etc.), you can configure the link checker to respect them:
// For Netlify _redirects file
linkValidator({
redirectsFile: '_redirects' // Path relative to build directory
})
// For Cloudflare Pages _redirects file (same format as Netlify)
linkValidator({
redirectsFile: '_redirects' // Cloudflare Pages uses same format
})
// For Vercel or custom location
linkValidator({
redirectsFile: '/path/to/redirects.json' // Absolute path
})Platform-Specific Examples
Netlify _redirects file:
/old-page /new-page 301
/blog/:slug /posts/:slug 301
/api/* https://api.example.com/v1/* 200
/docs/* /documentation/:splat 301Cloudflare Pages _redirects file:
# Same format as Netlify
/old-blog/* /blog/:splat 301
/admin /dashboard 302
/api/v1/* https://api.mysite.com/v1/:splat 200Vercel vercel.json redirects:
{
"redirects": [
{
"source": "/old-page",
"destination": "/new-page",
"permanent": true
}
]
}Note: Currently supports Netlify/Cloudflare
_redirectsformat. Vercel JSON support coming soon.
This prevents false positives when links are redirected rather than broken.
Redirect chains are followed up to 10 hops. If a link exceeds that — because two rules point at each other, or a rule redirects to itself — it's reported as broken with the reason invalid rather than being followed forever.
🚀 CI/CD Integration
Good news: Since link checking runs automatically during npm run build, it works out-of-the-box with all deployment platforms (Netlify, Cloudflare Pages, Vercel, etc.). No special configuration needed! 🎉
🔍 When CI/CD Configuration Matters
1. Build Failure Control
// Development: Don't fail builds on broken links
linkValidator({
failOnBrokenLinks: false // Let builds succeed for previews
})
// Production: Fail builds to prevent broken deployments
linkValidator({
failOnBrokenLinks: true // Block deployment if links are broken
})2. Environment-Specific Checking
// Different configs for different environments
linkValidator({
checkExternal: process.env.NODE_ENV === 'production',
verbose: process.env.NODE_ENV === 'development',
exclude: process.env.NODE_ENV === 'production'
? ['/admin/*', '/drafts/*'] // Production: exclude more
: ['/admin/*'] // Development: minimal exclusions
})3. Pull Request vs Main Branch
# GitHub Actions example for different branch behavior
- name: Build with link checking
run: npm run build
env:
# Strict checking on main, lenient on PRs
LINK_CHECK_MODE: ${{ github.ref == 'refs/heads/main' && 'strict' || 'lenient' }}4. Large Site Optimization
// Skip external links in CI for speed, but check locally
linkValidator({
checkExternal: !process.env.CI, // Only check external links locally
verbose: !!process.env.CI // Verbose in CI for debugging
})🔍 What Gets Checked
✅ Checked
- Internal page links:
/about,./contact.html,../index.html - Asset references:
/images/logo.png,/styles.css,/script.js - External links (when enabled):
https://example.com - Responsive images: All URLs in
srcsetattributes - Multiple elements:
<a>,<img>,<script>,<link>,<iframe>, etc.
❌ Skipped
- JavaScript URLs:
javascript:void(0) - Email/phone links:
mailto:,tel: - Data URLs:
data:image/png;base64,... - Blob URLs:
blob:https://example.com/...
🆘 Troubleshooting
🔧 Installation Issues
- Build failures during install? Check you have Node.js 18+ and npm/yarn latest version
🔍 Link Checking Issues
- Too many false positives? Add URLs to
excludearray - Build too slow? Set
checkExternal: false - Want more details? Set
verbose: true - Path traversal errors? This is a security feature - check your links for
../patterns - CI/CD failing? Ensure the build directory exists before link checking runs
💻 Programmatic Usage
import { checkLinks } from 'astro-link-validator';
const result = await checkLinks('./dist', {
checkExternal: true,
verbose: true
});
console.log(`Found ${result.brokenLinks.length} broken links`);🔄 Updating
To get the latest version:
npm update astro-link-validator🏗️ How It Works
- Build Hook: Uses Astro's
astro:build:donehook to run after your site is built - HTML Parsing: Scans all HTML files in the output directory using Cheerio
- Link Extraction: Finds all
hrefandsrcattributes from relevant HTML elements - Validation: Checks internal links against the file system and optionally validates external links via HTTP requests
- Reporting: Provides detailed, colored output showing exactly which links are broken and why
📝 License
MIT License - see the LICENSE file for details.
Made with ❤️ for the Astro community
