Adds 7 SEO improvements across the full stack: 1. Enhanced index.html: - Open Graph tags (og:title, og:description, og:image, og:locale sv_SE) - Twitter Card tags (summary_large_image) - Canonical URL (https://bilhej.se/) - Meta keywords, author, robots, language tags - Structured data in JSON-LD (Organization, WebSite, Service, FAQPage) - FAQ structured data with 4 Q&A pairs (pris, anonymitet, hur funkar det) - Preconnect hints for analytics origin 2. robots.txt: - Disallow: auth pages, admin, orders, payment, guest pages - Allow: public pages (/, /om-oss, /kontakt, /integritetspolicy, /villkor) - Crawl-delay: 10s - Points to sitemap 3. sitemap.xml: - 6 public URLs with proper changefreq and priority - Home (1.0), Om (0.7), Kontakt (0.6), GDPR/Villkor (0.5) 4. useSeo composable: - Dynamic document.title, meta description/OG/Twitter on route change - Canonical URL updates per page - Reset fallback for unnamed routes 5. Route-level SEO data: - 16 routes with unique title, description, keywords - Covers all public pages + key auth pages 6. App.vue: wired useSeo with route.name watcher (immediate: true) 7. nginx.conf: - Security headers: X-Frame-Options DENY, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy, Permissions-Policy, HSTS - Far-future cache (1y, immutable) for static assets - No-cache for robots.txt and sitemap.xml - Deny access to dotfiles - Stronger gzip config
95 lines
2.6 KiB
TypeScript
95 lines
2.6 KiB
TypeScript
/**
|
|
* useSeo — dynamically updates document <title> and meta tags on route change.
|
|
*
|
|
* Usage:
|
|
* const seo = useSeo()
|
|
* // On page mount / watch:
|
|
* watch(route, () => {
|
|
* seo.set({ title: 'Min sida', description: '...' })
|
|
* })
|
|
*
|
|
* The composable sets <title>, meta[name=description], meta[name=keywords],
|
|
* meta[property=og:title], meta[property=og:description], meta[name=twitter:title],
|
|
* and meta[name=twitter:description].
|
|
*/
|
|
|
|
export interface SeoMeta {
|
|
title: string
|
|
description: string
|
|
keywords?: string
|
|
ogTitle?: string
|
|
ogDescription?: string
|
|
canonical?: string
|
|
}
|
|
|
|
export function useSeo() {
|
|
function set(meta: SeoMeta) {
|
|
const title = meta.title ?? 'Bilhej'
|
|
const description =
|
|
meta.description ??
|
|
'Skicka brev till fordonsägare via registreringsnummer.'
|
|
|
|
// Document title
|
|
document.title = title
|
|
|
|
// Common meta tags
|
|
updateMeta('description', description)
|
|
if (meta.keywords) updateMeta('keywords', meta.keywords)
|
|
|
|
// Open Graph
|
|
updateMeta('og:title', meta.ogTitle ?? title, 'property')
|
|
updateMeta('og:description', meta.ogDescription ?? description, 'property')
|
|
|
|
// Twitter
|
|
updateMeta('twitter:title', meta.ogTitle ?? title, 'name')
|
|
updateMeta('twitter:description', meta.ogDescription ?? description, 'name')
|
|
|
|
// Canonical URL
|
|
updateCanonical(meta.canonical)
|
|
}
|
|
|
|
function reset() {
|
|
document.title = 'Bilhej — Skicka brev till fordonsägare'
|
|
updateMeta(
|
|
'description',
|
|
'Skicka ett fysiskt brev till en fordonsägare via registreringsnummer.',
|
|
)
|
|
updateMeta('og:title', 'Bilhej — Skicka brev till fordonsägare', 'property')
|
|
updateMeta(
|
|
'og:description',
|
|
'Skicka ett fysiskt brev till en fordonsägare via registreringsnummer.',
|
|
'property',
|
|
)
|
|
updateCanonical('https://bilhej.se/')
|
|
}
|
|
|
|
return { set, reset }
|
|
}
|
|
|
|
function updateMeta(
|
|
nameOrProperty: string,
|
|
content: string,
|
|
attr: 'name' | 'property' = 'name',
|
|
) {
|
|
let el = document.querySelector(
|
|
`meta[${attr}="${nameOrProperty}"]`,
|
|
) as HTMLMetaElement | null
|
|
if (!el) {
|
|
el = document.createElement('meta')
|
|
el.setAttribute(attr, nameOrProperty)
|
|
document.head.appendChild(el)
|
|
}
|
|
el.setAttribute('content', content)
|
|
}
|
|
|
|
function updateCanonical(href: string | undefined) {
|
|
let link = document.querySelector(
|
|
'link[rel="canonical"]',
|
|
) as HTMLLinkElement | null
|
|
if (!link) {
|
|
link = document.createElement('link')
|
|
link.setAttribute('rel', 'canonical')
|
|
document.head.appendChild(link)
|
|
}
|
|
link.setAttribute('href', href ?? 'https://bilhej.se/')
|
|
}
|