Skip to main content
FilaRank live SEO scoring in Filament

FilaRank, Filament’s SEO Toolkit

SEO toolkit for Filament v5: live content scoring, readability and GEO/AEO analysis, SERP preview, head-tag rendering, XML sitemaps, redirect management, AI fix suggestions, keyword research, rank tracking, and site-wide health reports.

Features

  • 26 analysis checks across three score groups:
    • SEO (13), title length, keyword in title / meta description / slug / first paragraph / subheadings, keyword density with stuffing detection, content length, cornerstone depth, internal links, outbound links, image alt text.
    • Readability (7), Flesch Reading Ease, sentence length distribution, paragraph length, passive voice, transition words, subheading distribution, repeated consecutive sentence openings.
    • GEO / AEO (6), direct-answer blocks, FAQ structure, entity clarity, citation signals, snippet-length meta, structured-data readiness.
  • Live analysis in the form, results re-render as the editor types (debounced), with traffic-light indicators and an overall 0–100 score.
  • AI Fix, one-click title / meta / slug suggestions with a before/after diff. Bring your own OpenAI, Gemini, or Anthropic key.
  • Google SERP snippet preview with truncation, right where the editor needs it.
  • SeoScoreColumn, a sortable red/amber/green badge for any Filament table.
  • Persisted scores, recalculated whenever the parent model is saved, so table badges never need a live re-analysis.
  • <x-filarank::tags />, one component that renders <title>, meta description, robots, canonical, Open Graph, Twitter Cards, and multi-type JSON-LD (@graph) with site-wide fallbacks.
  • XML sitemap, /filarank-sitemap.xml built from your scanned models, with optional search-engine pings.
  • Admin dashboards, SEO Overview, SEO Health (404s, cannibalization, orphans, under-linked cornerstones, technical audit), Keyword Research, Rank Tracker, Backlinks, Competitors, Redirects, and Settings. Every page is individually toggleable.
  • Framework-free analysis engine, src/Analysis and src/Support have zero Laravel dependencies, so the engine is trivially unit-testable and reusable outside Filament.
  • Interactive installer, php artisan filarank:install publishes assets, migrates, and wires the trait, form section, and score column into the models and resources you pick, with a --dry-run mode.
  • Fully translatable, publishable views, configurable checks.
New in v2.0: AI features, GEO/AEO checks, sitemaps, the schema registry, the technical audit, keyword research, rank tracking, backlinks, and competitors. See Upgrading from v1 if you already have FilaRank installed.

Requirements

  • PHP 8.3+
  • Laravel 11 / 12 / 13
  • Filament v5
Optional: laravel/head (Laravel 13.17+) if you would rather render tags through Laravel’s first-party @head directive. See Using Laravel Head.

Installation

The interactive installer publishes the config and migrations, offers to run migrate, then shows a checklist of your app/Models classes. For each model you pick it will:
  1. add the HasSeo trait (plus import) to the model,
  2. find the matching Filament resource anywhere under app/Filament, and, with your confirmation, append SeoFields::make() to the form and SeoScoreColumn::make() to the table, with imports.
Patching is deliberately conservative: files are only modified when the expected structure is found, every edit is idempotent (safe to re-run), and anything that can’t be patched automatically falls back to printed manual instructions. Use --dry-run to preview without writing:
Prefer to wire things up by hand? The manual steps below are exactly what the installer automates:
Optionally publish the config, views, or translations:

Register the panel plugin

The admin pages live behind a plugin so you only get the ones you want:
Every page defaults to on and can be switched off individually:

Usage

1. Prepare your model

By default the analyzer reads the model’s content and slug attributes and falls back to title for the page title. Override if your columns differ:

2. Add the SEO section to your Filament form

If your body field has a different name, or lives somewhere unusual in the schema tree:
SeoFields::make() binds to the seo relationship, so it must be used on a form with a record model that uses HasSeo.

3. Show the score in your table

Scores are recalculated and stored whenever the model is saved (disable with 'persist_score' => false).

4. Render the tags on your frontend

In your layout’s <head>:
Or for static pages without a model:

Using Laravel Head

Laravel 13.17 shipped laravel/head, a first-party way to manage the document <head>. FilaRank works with it, so you don’t have to choose between the panel you’re editing SEO in and the head management the rest of your app uses. It stays optional. FilaRank supports Laravel 11 and 12, laravel/head requires 13.17+, so it is a suggest rather than a require. Nothing changes if you don’t install it.
Then push the record’s stored meta before the view renders, instead of placing the component:
Static pages take the same overrides as the component:
Keep Laravel Head’s @head directive in your layout and drop <x-filarank::tags />. Render one or the other, never both, or every page gets two titles and two JSON-LD blocks. Both renderers read the same resolution chain (explicit argument, then the stored SEO row, then your model’s fallbacks, then site config) and emit the same title, description, canonical, robots, Open Graph, Twitter card, and JSON-LD graph. Two deliberate differences:
  • Laravel Head normalizes canonical URLs, forcing https and giving a root path its trailing slash. The component emits whatever you stored, verbatim.
  • FilaRank’s JSON-LD is scrubbed recursively before handover, because Laravel Head rejects null and empty-string values at any depth. This mostly matters if your model’s getProductSchema() returns partially-filled nested nodes.
SeoHead::for() throws if laravel/head isn’t installed. Guard with SeoHead::isAvailable() if you ship code that has to run either way.

5. Analyze anything programmatically

Or against a model directly: SeoAnalyzer::analyzeModel($post).

AI features

FilaRank is bring-your-own-key. Add a key under FilaRank SEO → SEO Settings (stored encrypted via Crypt), or fall back to env: OPENAI_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY. Every call is logged to filarank_ai_logs with provider, model, token counts, and estimated cost, surfaced as a monthly spend figure on the Overview dashboard. Nothing runs unless a key is configured, AI actions hide themselves when AiService::isConfigured() is false.

Choosing a model

The model dropdown in SEO Settings is built from config, and that list is the allowlist, add a model and it becomes selectable, with no code change and nothing hardcoded on the package side:
The first entry is the default when nothing has been chosen, so put your preferred model first. A saved selection is validated against the list on every read, so removing a model from config quietly falls back to that first entry rather than erroring. rates are USD per 1M tokens and drive the spend estimate. Key a rate by model name to price it exactly; anything without its own entry uses default. Getting this wrong only skews the dashboard figure, never a request. Two caveats when adding a model. Requests are built per provider, not per model, the OpenAI provider sends temperature and a JSON response format, so reasoning-style models that reject a custom temperature will fail with a 400. And because publishing the config replaces the package’s ai block wholesale (mergeConfigFrom merges only top-level keys), a published copy won’t inherit providers added in later versions. Either way, Test AI connection in Settings does a real round trip and surfaces the problem immediately. To use a provider directly, or in tests:
All provider failures are wrapped in AiRequestException with credentials stripped out, so a bad key or rate limit never leaks a secret into your logs or a notification.

Sitemap

Enable the sitemap in SEO Settings; while it’s off, the route returns 404 rather than an empty document. Entries are built from your scanned models, skipping records flagged noindex or without a public URL.
With pinging enabled, endpoints are called after a record is saved. Requests are deferred until after the response, so a slow or dead endpoint never delays a content save. The sitemap placeholder in each ping URL is replaced with the URL-encoded sitemap URL. Google retired its ping endpoint in 2023, so the list is deliberately easy to repoint.

Technical audit

SEO Health → Technical audit crawls the public URL of every record with SEO meta and reports missing titles, meta descriptions, canonicals, OG images, and focus keywords, plus broken status codes, absent JSON-LD, and images without alt text. The crawler only fetches URLs on your own app.url host. Extra hosts must be allow-listed and must resolve to public addresses, which stops a careless getSeoUrl() from pointing the crawler at internal services or cloud metadata endpoints. Blocked URLs are recorded as an unsafe_url issue so the meta checks still run and you can see why the fetch was skipped.
Runs are bounded by max_pages and atomic: issues are collected in memory and swapped in one transaction, so a crawl that dies halfway leaves your existing issue list intact.

Keyword research & rank tracking

Research pulls Google Autocomplete suggestions plus modifier expansions for a seed keyword, saves them as untracked rows, and lets you promote the ones you care about to tracked keywords. The table supports search, sorting, filtering by tracked state, and bulk track/dismiss.
Tracked keywords get daily position checks. SerpAPI is the supported provider, add the key in SEO Settings:
The command is scheduled automatically (rank_tracker.schedule_at, default 03:00) and exits cleanly with a warning when no key is configured, so an unconfigured install doesn’t fail every night. Scraping Google breaks their terms of service and stops working without warning, so it’s disabled unless you opt in with FILARANK_ALLOW_SCRAPE=true; until then the option is hidden from Settings. Research rows accumulate, so prune them when you’re done:
Tracked keywords are never pruned, they hold your rank history. Both are deliberately simple: you own the data, FilaRank enriches it. Backlinks imports a CSV export from Ahrefs, Semrush, or Search Console. Headers are matched flexibly (source_url/url/referring page, dr/domain_rating, and so on), and rows are matched on source + target URL, so re-importing a refreshed export updates rows instead of duplicating them. Optional AI toxicity scoring runs 25 unscored links at a time. Competitors is a domain list with an “Analyse gap” action that asks the model which keywords a competitor likely targets that you don’t. These are informed estimates from the domain and your own keyword list, not crawled ranking data, treat them as research prompts, not measurements.

Configuration

See config/filarank.php for site-wide defaults (site name, title separator, default description / share image, Twitter handle), tag-rendering toggles, schema types, disabled checks, and score persistence. Disable individual checks:
Choose which JSON-LD nodes are emitted (also toggleable from SEO Settings):
List the models FilaRank should scan for the sitemap, audit, cannibalization, and link graph:
Leave it empty and FilaRank falls back to any model that already has SEO meta rows, so the dashboards aren’t blank on a fresh install.

Security notes

  • API keys are encrypted at rest and shown masked in the UI. Paste a new value to replace one; leave the field blank to keep it.
  • Provider errors pass through Support\Redactor before being logged or displayed, stripping query-string credentials, bearer tokens, and known key formats.
  • The audit crawler is host-restricted (see Technical audit) and refuses redirects to untrusted hosts mid-request.
  • The fake AI provider is refused outside local and testing, so a misconfigured production panel can’t silently serve canned copy.
  • Google scraping is off by default and must be enabled explicitly in config.

Writing your own check

Implement the Check contract and register it:
Groups are seo, readability, and geo; anything else is ignored by the score panels.

Cross-record reports

All the cross-record logic lives in framework-free, unit-tested engines (src/Cannibalization, src/Linking, src/Redirects, src/Language, src/Keyphrase).

Redirect manager (404 capture + one-click 301s)

Register the middleware in bootstrap/app.php:
The middleware serves matching redirects (with normalisation, chain-following A→B→C, and loop protection) and logs every 404. Logged 404s appear on the SEO Health page with a one-click “Create redirect” link that pre-fills the source path. Rules are cached for an hour and the cache is busted automatically on create/edit/delete. Slug changes on published records create a 301 automatically. Reverting a slug clears the reverse rule, so renaming back and forth can’t leave a redirect loop behind. Configure with:

Internal linking suggestions & orphaned content

The SEO Health dashboard reports orphaned content (pages nothing links to) and under-linked cornerstones. The link graph compares URLs by normalised path, so absolute and relative links to the same page match. Programmatic access:

Keyword cannibalization warnings

Any keyphrase targeted by more than one record is flagged, sorted most-contested first. Keywords are normalised (case- and slug-insensitive), so “Coffee Beans”, “coffee beans”, and “coffee-beans” collide. Includes each record’s primary and related keyphrases.

Cornerstone content flagging

A Cornerstone content toggle in the SEO section marks your most important pages. Cornerstone records are held to a higher bar by the cornerstone-depth check (≥900 words, properly subheaded) and flagged on the dashboard when too few internal links point to them (config('filarank.cornerstone.min_incoming_links')).

Multiple focus keywords

A Related keyphrases tags field lets each record target secondary phrases. The primary keyword gets the full analysis; each related keyphrase gets the keyword-specific SEO checks (title / meta / slug / first paragraph / density / subheadings), shown in the analysis panel. Duplicates of the primary are de-duplicated automatically.

Per-language readability packs

Readability heuristics (transition words, passive-voice detection, syllable counting, reading-ease formula) sit behind a LanguagePack interface. English and Dutch ship in the box; a Content language selector per record picks the pack, defaulting to your app locale. Add your own:
Extend AbstractLanguagePack and implement transitionWords(), countSyllables(), and the passive-voice word lists, see EnglishPack and DutchPack for the pattern.

Styling

The panel UI is built from native Filament components plus hand-written filarank-* classes layered on Filament’s design tokens, inlined once per process. There is no Tailwind build step and no viteTheme requirement, the plugin looks right in any panel, light or dark, out of the box.

Testing

Roadmap ideas

  • More language packs (French, German, Spanish, …)
  • Bulk internal-link insertion from suggestions
  • AI content writer (draft full articles from keyword research)
  • IndexNow support alongside sitemap pings
  • Google Search Console import for real impression and position data

License

Commercial. One license covers one production project (its development and staging environments included). Purchase at Anystack.

Support

Email hello@usamamuneer.me. Full documentation: docs.filarank.com
Last modified on September 12, 2026