Static Site SEO Advantages
11ty (Eleventy) on GitHub Pages offers exceptional SEO foundations:
| Feature | SEO Benefit |
|---|---|
| Pre-rendered HTML | Instant crawlability |
| No database | Zero injection vulnerabilities |
| CDN delivery | Fast global load times |
| Minimal JavaScript | Better Core Web Vitals |
| Build-time optimization | Consistent output |
Core Web Vitals Targets
Performance Metrics
| Metric | Target | What It Measures |
|---|---|---|
| LCP (Largest Contentful Paint) | < 2.5s | Main content load speed |
| INP (Interaction to Next Paint) | < 200ms | Response to user input |
| CLS (Cumulative Layout Shift) | < 0.1 | Visual stability |
LCP Optimization
Target: Under 2.5 seconds on mobile 3G
<!-- Preload critical resources -->
<link rel="preload" href="/css/main.css" as="style">
<link rel="preload" href="/assets/fonts/main.woff2" as="font" crossorigin>
<!-- Inline critical CSS -->
<style>
/* Above-the-fold styles */
body { font-family: system-ui, sans-serif; }
.header { /* minimal header styles */ }
</style>
<!-- Defer non-critical CSS -->
<link rel="stylesheet" href="/css/main.css" media="print" onload="this.media='all'">
CLS Prevention
Target: Near zero layout shift
<!-- Always specify image dimensions -->
<img src="/assets/images/hero.jpg"
width="800"
height="400"
alt="Description">
<!-- Reserve space for dynamic content -->
<div style="min-height: 200px;">
<!-- Content that loads later -->
</div>
INP Optimization
Target: Under 200ms interaction response
<!-- Defer non-critical JavaScript -->
<script src="/assets/js/main.js" defer></script>
<!-- Avoid blocking main thread -->
<!-- Use requestIdleCallback for non-urgent tasks -->
XML Sitemap Configuration
11ty Sitemap Generation
Install plugin:
npm install @11ty/eleventy-plugin-rss
Configure in .eleventy.js:
const pluginRss = require("@11ty/eleventy-plugin-rss");
module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(pluginRss);
// Add sitemap collection
eleventyConfig.addCollection("sitemap", function(collection) {
return collection.getAll().filter(item => {
// Exclude drafts and private pages
return !item.data.draft && !item.data.private;
});
});
};
Create sitemap.njk:
---
permalink: /sitemap.xml
eleventyExcludeFromCollections: true
---
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{%- for page in collections.sitemap %}
<url>
<loc>{{ site.url }}{{ page.url }}</loc>
<lastmod>{{ page.date | dateToRfc3339 }}</lastmod>
<changefreq>{{ page.data.changefreq | default: "monthly" }}</changefreq>
<priority>{{ page.data.priority | default: "0.5" }}</priority>
</url>
{%- endfor %}
</urlset>
Sitemap Submission
- Go to Search Console
- Sitemaps > Add sitemap
- Enter:
sitemap.xml - Submit
Canonical URL Configuration
11ty Implementation
In base layout (_includes/layouts/base.njk):
<head>
{% if canonical %}
<link rel="canonical" href="{{ canonical }}">
{% else %}
<link rel="canonical" href="{{ site.url }}{{ page.url }}">
{% endif %}
</head>
Frontmatter override:
---
title: "Page Title"
canonical: "https://example.org/preferred-url/"
---
Canonical Rules
| Scenario | Canonical URL |
|---|---|
| Standard page | Self-referencing |
| Paginated content | First page in series |
| Duplicate content | Preferred version |
| HTTP/HTTPS variants | HTTPS version |
| Trailing slash variants | Choose one, be consistent |
Mobile-First Indexing
Requirements Checklist
- [ ] Responsive design (no separate mobile site)
- [ ] Same content on mobile and desktop
- [ ] Viewport meta tag configured
- [ ] Touch targets ≥ 48px
- [ ] Readable font sizes (≥ 16px)
- [ ] No horizontal scrolling
Viewport Configuration
<meta name="viewport" content="width=device-width, initial-scale=1">
Touch Target Sizing
/* Minimum touch target size */
a, button, input, select {
min-height: 48px;
min-width: 48px;
}
/* Adequate spacing */
.nav-links a {
padding: 12px 16px;
margin: 4px;
}
robots.txt Configuration
Create robots.txt in src/:
User-agent: *
Allow: /
# Block admin/draft areas
Disallow: /admin/
Disallow: /drafts/
# Sitemap location
Sitemap: https://example.org/sitemap.xml
Internal Linking Strategy
11ty URL Handling
Use inputPathToUrl filter for reliable links:
<a href="{{ '/know-your-rights/home-raids.md' | inputPathToUrl }}">
Home Raids Guide
</a>
Link Structure Best Practices
| Practice | Why |
|---|---|
| Use descriptive anchor text | Better context for crawlers |
| Link to related content | Distributes page authority |
| Consistent URL format | Avoids duplicate issues |
| Check for broken links | Maintains crawl efficiency |
Related Content Linking
In content pages:
## Related Resources
- [Workplace Rights](/know-your-rights/workplace-enforcement/)
- [Checkpoint Guide](/know-your-rights/checkpoints/)
- [Find Legal Aid](/resources/legal-documents/)
Redirect Handling
GitHub Pages Limitations
GitHub Pages doesn't support .htaccess or server-side redirects.
Options:
- HTML meta refresh:
---
permalink: /old-url/
---
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="0; url=/new-url/">
<link rel="canonical" href="/new-url/">
</head>
<body>
<p>Redirecting to <a href="/new-url/">new page</a>...</p>
</body>
</html>
- JavaScript redirect:
<script>
window.location.href = '/new-url/';
</script>
Redirect Best Practices
- Use 301-equivalent (permanent) redirects
- Update internal links to avoid chains
- Monitor for 404 errors in Search Console
- Maintain redirects for at least 1 year
Image Optimization
Responsive Images
<picture>
<source srcset="/assets/images/hero-800.webp" type="image/webp">
<source srcset="/assets/images/hero-800.jpg" type="image/jpeg">
<img src="/assets/images/hero-800.jpg"
alt="Descriptive alt text"
width="800"
height="400"
loading="lazy">
</picture>
Image Build Pipeline
Install sharp for image processing:
npm install @11ty/eleventy-img
Configure image shortcode:
const Image = require("@11ty/eleventy-img");
async function imageShortcode(src, alt, sizes) {
let metadata = await Image(src, {
widths: [400, 800, 1200],
formats: ["webp", "jpeg"],
outputDir: "./_site/assets/images/",
urlPath: "/assets/images/"
});
return Image.generateHTML(metadata, {
alt,
sizes,
loading: "lazy",
decoding: "async"
});
}
Structured Data Testing
Validation Tools
| Tool | Purpose |
|---|---|
| Rich Results Test | Validate schema, preview rich results |
| Schema Validator | Check syntax compliance |
| Search Console | Monitor errors in production |
Build-Time Validation
Add schema validation to CI/CD:
# Example with ajv-cli
npx ajv validate -s schema.json -d _site/**/*.json
Technical SEO Checklist
Build Configuration
- [ ] XML sitemap generation
- [ ] robots.txt configured
- [ ] Canonical tags on all pages
- [ ] Image optimization pipeline
- [ ] CSS/JS minification
- [ ] Gzip/Brotli compression via CDN
Page-Level
- [ ] Title tags unique and descriptive (50-60 chars)
- [ ] Meta descriptions compelling (150-160 chars)
- [ ] H1 tag present and unique
- [ ] Images have alt text
- [ ] Internal links use descriptive anchor text
- [ ] Schema markup validated
Performance
- [ ] LCP < 2.5s on mobile
- [ ] CLS < 0.1
- [ ] INP < 200ms
- [ ] Mobile usability passing
- [ ] No console errors
Monitoring
- [ ] Search Console configured
- [ ] Sitemap submitted
- [ ] Core Web Vitals tracking
- [ ] Crawl error monitoring
- [ ] Index coverage review
Next Steps
- Set up analytics - Privacy-preserving tracking
- Review schema markup - Validate implementation
- Check keyword research - Target optimization