seo — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited seo (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
@rules/laravel/laravel.mdc for routing, Blade, and asset conventions.@rules/security/frontend.md — never inject unsanitized user content into JSON-LD, meta tags, or <head>; escape every dynamic value.robots.txt allows important pages and blocks low-value surfaces (e.g. /admin, filtered listing permutations). In Laravel, serve a static public/robots.txt or a route that renders one per environment (block everything on staging).noindex.Redirect:: rules and web-server rewrites for stacked redirects.www vs apex). Enforce with a single canonical host and HTTPS redirect.route('post.show', $post).hreflang when locales exist.spatie/laravel-sitemap package to crawl and emit sitemap.xml.Walk these every audit; each item maps to a concrete file, route, or response to inspect — never report a category without naming where it occurs.
| Check | What to look for | Where in Laravel |
|---|---|---|
| Crawl errors | 4xx/5xx on linked URLs | server logs, route list, php artisan route:list |
| Broken links | dead internal/outbound hrefs | Blade views, content models, a crawl pass |
| Redirect chains | 2+ stacked hops | Redirect:: rules, web-server rewrites |
| Mixed content | http:// assets on an HTTPS page | Blade asset refs; force asset()/secure_url() |
| Duplicate content | same intent on multiple URLs | facets, pagination, casing/slash variants |
| Thin content | low-value near-empty pages | auto-generated tag/archive pages |
| Orphan pages | published but unlinked internally | compare sitemap/routes against internal links |
href pointing at it cannot be discovered by crawlers following links — surface it and add an internal link or drop it from the sitemap.http:// asset is downgraded and may be blocked by the browser. Derive asset URLs from asset() / secure_url() so they inherit the request scheme.{{-- resources/views/layouts/app.blade.php (inside <head>) --}}
<title>@yield('title', config('app.name'))</title>
<meta name="description" content="@yield('meta_description', '')">
<link rel="canonical" href="{{ $canonical ?? url()->current() }}">
@hasSection('noindex')
<meta name="robots" content="noindex,nofollow">
@endif
@stack('schema'){{-- a page view --}}
@section('title', $post->title . ' | ' . config('app.name'))
@section('meta_description', Str::limit(strip_tags($post->excerpt), 155))
@php($canonical = route('post.show', $post))Targets: LCP < 2.5s, INP < 200ms, CLS < 0.1.
@vite) so they cache long-term. Use loading="eager" and fetchpriority="high" on the LCP image; loading="lazy" on below-the-fold images.width/height on images, and avoid injecting content above existing content. Tailwind aspect-* utilities help reserve space.font-display: swap.Emit JSON-LD in the layout via a pushed stack so each page contributes its own schema. Match schema to content that is actually present.
Organization or local business schema where appropriate.Article / BlogPosting.Product and Offer.BreadcrumbList.FAQPage only when the content truly matches.{{-- a page view --}}
@push('schema')
<script type="application/ld+json">
{!! json_encode([
'@context' => 'https://schema.org',
'@type' => 'Article',
'headline' => $post->title,
'author' => ['@type' => 'Person', 'name' => $post->author->name],
'publisher'=> ['@type' => 'Organization', 'name' => config('app.name')],
'datePublished' => $post->published_at->toIso8601String(),
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) !!}
</script>
@endpushBuild the array with json_encode (never string-concatenate user input) so values are escaped and the document stays valid. Validate the emitted markup with Google's Rich Results Test before relying on it.
H1 per page.H2/H3 reflect real content hierarchy; do not pick heading levels for visual styling.Title: Primary Topic - Specific Modifier | Brand
Meta: Action + topic + value proposition + one supporting detailUse competitors to find concrete, page-level opportunities — never to copy.
[gap type] target query/topic → owning URL → concrete change, so it slots straight into the audit output below.Search engines reward Experience, Expertise, Authoritativeness, and Trustworthiness. Tie each signal to something on the page.
Article.author in JSON-LD plus a visible byline) and first-hand detail, not paraphrased generalities.Every recommendation needs a measurable outcome; pick the metrics that match the change and capture a baseline before shipping.
Validate audits and findings with real tools rather than asserting them:
php artisan route:list for the public surface, a sitemap build for the intended index set.[HIGH] Duplicate title tags on product pages
Location: resources/views/products/show.blade.php
Issue: @section('title') falls back to the app name for every product, weakening relevance and creating duplicate signals.
Fix: Render a unique title from the product name and primary category.
Measure: distinct title coverage in Search Console; expect duplicate-title warnings to drop.| Anti-pattern | Fix |
|---|---|
| keyword stuffing | write for users first |
| thin near-duplicate pages | consolidate or differentiate them |
| schema for content that is not present | match schema to reality |
| advice without checking the actual page | read the real Blade view first |
| generic "improve SEO" output | tie every recommendation to a page or asset |
| hand-built canonical URLs | derive from named routes |
| buying or exchanging links | earn links through useful content |
| recommendation with no metric | state the metric and baseline to verify it |
noindex.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.