Files
wiki/netlify.toml
Wayne Sutton 98916899a3 fix(seo): resolve 7 SEO issues from GitHub Issue #4
SEO improvements for better search engine optimization:

  1. Canonical URL - Added client-side dynamic canonical link tags for posts and pages
  2. Single H1 per page - Markdown H1s demoted to H2 with .blog-h1-demoted class (maintains H1 visual styling)
  3. DOM order fix - Article now loads before sidebar in DOM for SEO (CSS order property maintains visual layout)
  4. X-Robots-Tag - HTTP header added via netlify.toml (index, follow for public; noindex for dashboard/api routes)
  5. Hreflang tags - Self-referencing hreflang (en, x-default) for language targeting
  6. og:url consistency - Uses same canonicalUrl variable as canonical link tag
  7. twitter:site - New TwitterConfig in siteConfig.ts for Twitter Cards meta tags

  Files modified:
  - src/config/siteConfig.ts: Added TwitterConfig interface with site/creator fields
  - src/pages/Post.tsx: SEO meta tags for posts/pages, DOM order optimization
  - src/components/BlogPost.tsx: H1 to H2 demotion in markdown renderer
  - src/styles/global.css: .blog-h1-demoted class, CSS order properties
  - convex/http.ts: hreflang and twitter:site in generateMetaHtml()
  - netlify.toml: X-Robots-Tag headers for public, dashboard, API routes
  - index.html: canonical, hreflang, twitter:site placeholder tags
  - fork-config.json.example: twitter configuration fields

  Closes #4

  Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 11:31:55 -08:00

134 lines
2.9 KiB
TOML

[build]
command = "npm ci --include=dev && npx convex deploy --cmd 'npm run build'"
publish = "dist"
[build.environment]
NODE_VERSION = "20"
# Raw markdown passthrough - explicit rule prevents SPA fallback from intercepting
[[redirects]]
from = "/raw/*"
to = "/raw/:splat"
status = 200
force = true
# Static files passthrough
[[redirects]]
from = "/assets/*"
to = "/assets/:splat"
status = 200
force = true
# SPA fallback for client-side routing (must be last)
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
# Edge functions for dynamic Convex HTTP proxying
# RSS feeds
[[edge_functions]]
path = "/rss.xml"
function = "rss"
[[edge_functions]]
path = "/rss-full.xml"
function = "rss"
# Sitemap
[[edge_functions]]
path = "/sitemap.xml"
function = "sitemap"
# API endpoints
[[edge_functions]]
path = "/api/posts"
function = "api"
[[edge_functions]]
path = "/api/post"
function = "api"
[[edge_functions]]
path = "/api/export"
function = "api"
# Geo location API for visitor map
[[edge_functions]]
path = "/api/geo"
function = "geo"
# MCP Server endpoint for AI tool integration
# Rate limiting configured in edge function config (50 req/min per IP)
[[edge_functions]]
path = "/mcp"
function = "mcp"
# Open Graph bot detection for social preview cards only
# Excludes raw markdown, static assets, and AI-consumable files
[[edge_functions]]
path = "/*"
function = "botMeta"
excludedPath = [
"/raw/*",
"/assets/*",
"/api/*",
"/.netlify/*",
"/favicon.ico",
"/favicon.svg",
"/robots.txt",
"/sitemap.xml",
"/rss.xml",
"/rss-full.xml",
"/llms.txt",
"/openapi.yaml"
]
# Security and SEO headers (global, excludes /raw/*)
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
X-XSS-Protection = "1; mode=block"
Referrer-Policy = "strict-origin-when-cross-origin"
X-Robots-Tag = "index, follow"
# Link header removed from global scope to avoid applying to /raw/*
# Dashboard - prevent indexing of admin pages
[[headers]]
for = "/dashboard/*"
[headers.values]
X-Robots-Tag = "noindex, nofollow"
# API endpoints - prevent indexing
[[headers]]
for = "/api/*"
[headers.values]
X-Robots-Tag = "noindex"
# Link header for SPA entry point only
[[headers]]
for = "/index.html"
[headers.values]
Link = "</llms.txt>; rel=\"author\""
# Raw markdown files - AI friendly headers
# No Link header, no noindex - optimized for AI crawlers
[[headers]]
for = "/raw/*"
[headers.values]
Content-Type = "text/plain; charset=utf-8"
Access-Control-Allow-Origin = "*"
Cache-Control = "public, max-age=3600"
# Removed X-Robots-Tag = "noindex" - AI crawlers need to index raw content
[context.production.environment]
NODE_ENV = "production"
[context.deploy-preview.environment]
NODE_ENV = "development"
[context.branch-deploy.environment]
NODE_ENV = "development"