Download the PHP package ismailnakkar/laravel-seo without Composer
On this page you can find all versions of the php package ismailnakkar/laravel-seo. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download ismailnakkar/laravel-seo
More information about ismailnakkar/laravel-seo
Files in ismailnakkar/laravel-seo
Package laravel-seo
Short Description Head tags, canonical URLs, robots.txt, sitemaps, IndexNow and crawlability tests for Laravel.
License MIT
Homepage https://github.com/ismailnakkar/laravel-seo
Informations about the package laravel-seo
Laravel SEO
Head tags, canonical URLs, robots.txt, sitemaps and IndexNow for Laravel, driven by config/seo.php. Every URL it
emits is built on your site's origin, never the request host. Ships crawler-style test assertions and seo:check, a
live audit.
Requires PHP 8.4+ and Laravel 12.61.1+ or 13.12+. See CHANGELOG.md.
Install
seo:install publishes the config and deletes public/robots.txt, public/sitemap.xml and public/indexnow-key.txt,
which the web server would serve instead of the package's routes. Set APP_URL (or seo.url) to the public origin.
An nginx location = /robots.txt block needs try_files $uri /index.php?$query_string;, or robots.txt answers 404.
The package registers GET /robots.txt, /sitemap.xml, /sitemap-{n}.xml and /indexnow-key.txt (an app route on
the same URI wins; 'routes' => false turns them off), a global NoindexHosts middleware, <x-seo::head />, @seo,
and the seo:install, seo:check and seo:indexnow commands.
Usage
Put the head at the top of your layout's <head>, and remove the layout's own title, description, robots, canonical
and Open Graph tags:
Describe each page in its view:
In a component layout, pass them as props instead: <x-seo::head :title="$title ?? null" :description="$description ?? null" />.
From a controller, inject Seo\Seo and call $seo->page(...) with the same arguments.
| Parameter | Default | Effect |
|---|---|---|
title |
prop, section, name |
<title> and og:title, suffixed with title_separator and name. |
description |
prop, section | Meta and og:description. |
image |
config image |
og:image: a URL or a path on url. |
canonical |
request path on url |
Replaces the canonical and drops hreflang. Prefer a root-relative path. |
robots |
Robots::index |
Robots::noindex (noindex, follow) or Robots::none (noindex, nofollow). In a view, write \Seo\Robots::noindex. |
paginated |
false |
Keep ?page=N (N ≥ 2) in the canonical. |
suffixSiteName |
true |
false leaves the title unsuffixed. |
jsonLd |
[] |
schema.org nodes (arrays or JsonSerializable). The home page also gets Organization and WebSite. |
Calls merge: each non-blank argument replaces its field, so a view's @seo beats its controller's page(), and
jsonLd appends.
Good to know:
<x-seo::head />prints a marker that is filled into the HTTP response, so@seoworks anywhere in the views. A call after the response is built (middleware after$next()) is lost, and HTML rendered to a string (mail, PDF,artisan down --render,$this->view()in tests) keeps the marker.- Error responses (4xx, 5xx) render
noindexand ignore@seo. - Use
Robots::noindexon utility pages (password reset, email verification) andRobots::noneon pages linking to user-submitted URLs. - If the site config cannot be built (a throwing
siteUsing(), an invalidurl), it throws outside production. In production the error is reported and pages are served with only a<title>andnoindex, nofollow, uncached. - Customise the markup with
php artisan vendor:publish --tag=seo-views. - Livewire:
@seoin the view or$seo->page()inmount(). Inertia: call$seo->page()in the controller.
Sitemaps
List route names, paths or URLs in config: 'sitemap' => ['home', 'pricing', '/about']. For database entries,
register a resolver in a service provider's boot(Seo $seo):
Resolver entries come first and replace a config entry with the same URL. Set lastModified to the content's real
last change, never now(). Past 50,000 URLs /sitemap.xml becomes an index of /sitemap-{n}.xml. With nothing
listed, /sitemap.xml is a 404.
robots.txt
Built from disallow (end each prefix in /). Only the url host lists the sitemap. It keeps answering 200 during
artisan down, and serves allow-all if your config throws.
Hosts
- Other hosts the app answers on canonicalise to
url. Redirect www and alias hosts tourlwith a 301 yourself. noindex_hostsresponses carryX-Robots-Tag: noindex, nofollow. nginx serves static files without PHP, so add the header there too:map $host $seo_noindex { dl.example.com "noindex, nofollow"; default ""; }inhttp, andadd_header X-Robots-Tag $seo_noindex always;inserver.- On Laravel 13, a
Route::domain()route matches before the package's; keep a domain catch-all off dots with->where('slug', '[^.]+').
IndexNow
Set INDEXNOW_KEY to 8–128 letters, digits or dashes. After a deploy, submit what changed:
Overrides
For values a config file cannot hold, such as admin-edited settings, return overrides from a closure in
boot(Seo $seo). Its parameters are injected. It runs on every request and in the console, so cache what it reads and
never read the request.
Null keys keep their config value; sitemap and routes always come from config.
Multilingual sites
ismailnakkar/laravel-localization plugs in canonicals, hreflang and sitemap alternates with no configuration. For any other setup, register a closure that returns, for a matched route and its path, the page's own path and each language's path:
alternates is in hreflang order, default language first (it becomes x-default). Return paths only: a scheme, ?
or # throws. Pages with a canonical override or served noindex emit no alternates. For sitemap URLs the route is
matched but not bound: read $path, never $route->parameter().
Testing
Use the Seo\Testing\SeoAssertions trait. Helpers fetch through the kernel as Googlebot without cookies, so
actingAs() neither reaches a helper nor survives one.
| Method | Asserts |
|---|---|
robotsTxt($host) |
robots.txt answers 200 with your config's body. Returns a matcher with allows($token, $path). |
followRedirectChain($url, $maxHops = 10, $userAgent = Googlebot, $headers = []) |
Follows redirects, failing on a loop or too many hops. Returns the last response. |
assertCrawlable($url, $maxHops = 3) |
200, indexable, one <title>, one self-referencing canonical, no SVG og:image. |
assertNotIndexable($response) |
X-Robots-Tag or robots meta says noindex. |
assertNoindexNotDisallowed(...$urls) |
Each URL is noindex and allowed by robots.txt. |
assertNoStaticShadows(...$extraPaths) |
public/robots.txt, public/sitemap.xml, public/indexnow-key.txt and any extra full paths do not exist. |
assertSitemapComplete() |
Every sitemap URL is on-host, unique, allowed and crawlable, with unique titles and descriptions per language. |
assertHreflangReciprocal($url) |
Every alternate answers 200, is self-canonical, lists the same set and renders its own <html lang>. |
seo:check
Audits the deployed site as a crawler does: robots.txt, sitemap, sample pages, the home page's JSON-LD and logo, AI
crawler access, http:// and www. redirects, X-Robots-Tag on noindex hosts, and cookieless --link chains. It
exits 1 on any FAIL. It compares against local config, so run it with production's values:
Configuration
Every key in config/seo.php is optional; a blank value counts as unset, except in routes, where it turns the routes
off.
| Key | Default | |
|---|---|---|
name |
app.name |
Title suffix, og:site_name, Organization and WebSite name. |
url |
app.url |
The origin every URL is built on. |
title_separator |
' · ' |
Between the page title and name. |
index_by_default |
true |
false: pages without @seo render noindex, follow. |
image |
null |
Default share image, ideally 1200×630. |
logo |
null |
Organization logo on the home page, at least 112×112. |
organization_type |
'Organization' |
schema.org subtype, such as OnlineBusiness. |
alternate_names |
[] |
Organization and WebSite alternateName. |
same_as |
[] |
Organization sameAs profile URLs. |
google_verification |
SEO_GOOGLE_VERIFICATION |
google-site-verification meta on the home page. |
disallow |
[] |
robots.txt path prefixes. |
noindex_hosts |
[] |
Hosts served noindex, nofollow. |
sitemap |
[] |
Route names, paths or URLs. |
index_now_key |
INDEXNOW_KEY |
The IndexNow key. |
routes |
true |
false: serve the files yourself (name your key route seo.indexnow). |
Not included
llms.txt, changefreq/priority, meta keywords, rel=next/prev, snippet limits, twitter:* beyond twitter:card,
typed schema.org helpers, host redirects and subdirectory installs.
License
MIT. See LICENSE.
All versions of laravel-seo with dependencies
ext-dom Version *
guzzlehttp/psr7 Version ^2.9 || ^3.0
laravel/framework Version ^12.61.1 || ^13.12
symfony/polyfill-intl-idn Version ^1.18