Przejdź do treści

Problemy skanowania — rejestr kodów

Każdy problem zgłaszany przez skanowanie Pro ma stabilny kod z jednego rejestru: Rankbeam\Seo\Pro\Scanning\IssueRegistry. Skanery nigdy nie tworzą kodów doraźnie. Budują każdy problem przez IssueRegistry::make(), które przypisuje ważność i pole z rejestru oraz odrzuca każdy niezdefiniowany kod. Poniższy katalog jest więc kontraktem, na którym możesz budować: panele, eksporty i ocena Pro odczytują kody zamiast analizować tekst komunikatów. Bezpłatny seo:audit używa własnego rejestru metadanych rdzenia, z węższym zakresem i częściowo innymi kodami hreflang.

Każdy kod zawiera:

  • id — stabilny ciąg zapisany jako seo_scan_issues.issue_type.
  • severitycritical, warning lub notice; stała dla danego kodu. Zamiast zmieniać ważność, rozdzielamy kod.
  • field — pole seo_meta, którego dotyczy problem, lub page dla problemów całej strony.
  • execution class — dane potrzebne do wykrycia problemu (poniżej).
  • evidence — klucze w tablicy context problemu.

Klasy wykonania

Każda kontrola należy do dokładnie jednej z trzech klas, zależnie od wymagań wykonania:

KlasaWymaganiaKto ją wykonuje
metadatamodel i resolver rdzenia, bez pobierania stronyskanowanie modelu (PageScanner); bezpłatny seo:audit obejmuje część kontroli metadanych
renderedHTML zwracany przez stronę (żądanie do jądra w tym samym procesie lub zewnętrzne pobranie)skanowanie URL-a (UrlScanner)
networkpobranie wychodzące w celu sprawdzenia osobnego adresu docelowego (adres kanoniczny wskazujący gdzie indziej)skanowanie URL-a, zawsze przez SsrfGuard

Dlatego bezpłatny audyt w procesie aplikacji nigdy nie dorówna pełnemu skanowaniu Pro: tylko kody metadata można obliczyć bez renderowania strony, a tylko proces Pro pobiera wyrenderowany HTML i sprawdza kanoniczne adresy docelowe przez sieć. Filtruj rejestr według klasy przez IssueRegistry::byClass(IssueRegistry::EXEC_METADATA).

Kody metadanych

Wykrywane z modelu i resolvera przez PageScanner. missing_title, missing_description i kody długości są też emitowane przez skanowanie wyrenderowanego URL-a, które mierzy zwracany <head>. Kody i znaczenie pozostają takie same.

KodWażnośćPoleDowodyZnaczenie
missing_titlecriticaltitleBrak tytułu i możliwej do wyliczenia wartości zastępczej.
missing_descriptionwarningdescriptionBrak metaopisu i możliwej do wyliczenia wartości zastępczej.
missing_og_imagenoticeog_imageBrak obrazu Open Graph i możliwej do wyliczenia wartości zastępczej.
missing_focus_keywordnoticefocus_keywordsNie ustawiono głównego słowa kluczowego.
duplicate_titlewarningtitletitle, duplicate_urlsTytuł jest używany na innych stronach w tym samym locale.
duplicate_descriptionwarningdescriptiondescription, duplicate_urlsOpis jest używany na innych stronach w tym samym locale.
title_too_longwarningtitlelength, max, scriptRozstrzygnięty tytuł przekracza zalecenie dla systemu pisma (60 dla alfabetu łacińskiego, około 30 dla CJK).
title_too_shortnoticetitlelength, min, scriptRozstrzygnięty tytuł jest poniżej dolnego progu systemu pisma (30 dla alfabetu łacińskiego, około 15 dla CJK).
description_too_longwarningdescriptionlength, max, scriptRozstrzygnięty opis przekracza zalecenie dla systemu pisma (160 / około 80).
description_too_shortnoticedescriptionlength, min, scriptRozstrzygnięty opis jest poniżej dolnego progu systemu pisma (70 / około 35).
robots_conflict_indexingcriticalrobotsrobotsDyrektywa robots zawiera zarówno index, jak i noindex.
robots_conflict_followingwarningrobotsrobotsDyrektywa robots zawiera zarówno follow, jak i nofollow.
noindex_warningwarningrobotsrobots, canonical, page_url, shipping_signalStrona z adresem kanonicznym wskazującym na siebie jest noindex. To heurystyka do sprawdzenia, a nie dowód, że strona musi być indeksowana. Emitowana przy skanowaniu modelu i wyrenderowanego URL-a.
invalid_canonicalcriticalcanonicalcanonicalWartość adresu kanonicznego nie jest prawidłowym URL-em.
cross_domain_canonicalwarningcanonicalcanonical, page_urlAdres kanoniczny wskazuje inny host niż strona.
shared_canonicalnoticecanonicalcanonicalKilka stron deklaruje ten sam adres kanoniczny.
insecure_canonicalwarningcanonicalcanonicalAdres kanoniczny http:// w witrynie https.
hreflang_invalid_codewarningalternatesinvalid_codesWariant hreflang używa wartości, która nie jest ani x-default, ani poprawnym kodem języka BCP-47.
hreflang_missing_self_referencewarningalternateslocale, page_urlZadeklarowano warianty, ale żaden nie wskazuje własnego locale strony (brak hreflang do siebie).
hreflang_duplicate_codewarningalternatesduplicate_codesTen sam kod hreflang wskazuje więcej niż jeden URL (niejednoznaczna grupa).
hreflang_missing_x_defaultnoticealternateslanguagesWielojęzyczna grupa hreflang nie ma wariantu zastępczego x-default.
aeo_missing_authornoticeschemaArtykuł w danych strukturalnych strony nie ma encji autora (schemat nie wskazuje jawnie autorstwa/pochodzenia).
aeo_article_missing_datenoticeschemaArtykuł w danych strukturalnych strony nie ma daty publikacji/modyfikacji (schemat nie wskazuje jawnie chronologii artykułu).

Progi długości pochodzą z zasad długości rdzenia uwzględniających system pisma (Pro 2.33): 60/160 dla alfabetu łacińskiego, około 30/80 dla CJK, liczone w grafemach. Skanowanie nie przeczy więc licznikom znaków w edytorze. Dolne progi (tytuł 30, opis 70 dla alfabetu łacińskiego, około połowy tych wartości dla CJK) oznaczają przyjęty przez skaner dolny poziom optymalizacji, a klucz kontekstu script wskazuje zastosowaną kategorię. Długość jest mierzona dla rozstrzygniętego tytułu/opisu, czyli wartości faktycznie renderowanej, wraz z wartościami zastępczymi i sufiksem tytułu.

Kody hreflang_* sprawdzają zadeklarowane warianty hreflang strony, odczytane z alternates resolvera: nieprawidłowe lub powielone kody, brak odwołania do siebie i brak x-default w grupie wielojęzycznej. Działają tylko wtedy, gdy strona deklaruje warianty. Te kontrole metadanych nie sprawdzają wzajemności odwołań między stronami („return tags”). Opcjonalna kontrola sieciowa poniżej pobiera drugą stronę.

Kody aeo_* są sygnałami gotowości do odpowiedzi (AEO): czy treść artykułu jest czytelna w danych strukturalnych? Odczytują rozstrzygnięty graf JSON-LD i uruchamiają się tylko wtedy, gdy deklaruje dane strukturalne typu artykuł (Article, BlogPosting, NewsArticle, …), którym brakuje encji author (jawnego autorstwa/pochodzenia) lub datePublished / dateModified (jawnej chronologii). Strona bez artykułu nigdy nie jest oznaczana. Steruje nimi seo-pro.scan.checks.aeo, domyślnie włączone, a ich odpowiednik znajduje się w bezpłatnym seo:audit.

missing_focus_keyword wymaga włączenia

Uwaga o głównym słowie kluczowym pojawia się tylko wtedy, gdy w rdzeniu włączono proces słów kluczowych (seo.keywords.enabled, domyślnie false). Gdy jest wyłączony, skanowanie nie zgłasza braku słowa kluczowego. Bezpłatne polecenie seo:audit i edytor Filament odczytują tę samą opcję rdzenia, więc skanowanie, audyt i przypomnienie edytora zawsze są zgodne. Istnieje tylko jeden warunek włączenia.

Kody wyrenderowanej strony

Wykrywane przez UrlScanner ze zwracanego HTML. Dla celów na tym samym hoście używane jest żądanie do jądra w tym samym procesie, bez ruchu wychodzącego, a dla celów zewnętrznych — pobranie objęte zabezpieczeniami.

KodWażnośćPoleDowodyZnaczenie
http_errorcriticalpagestatusURL zwrócił status 4xx/5xx.
empty_responsecriticalpageURL zwrócił pustą treść odpowiedzi.
missing_canonicalnoticecanonicalBrak <link rel="canonical"> w wyrenderowanej sekcji head.
noindex_pagenoticerobotsrobotsWyrenderowana strona jest noindex (informacja). Strona noindex, która ma też adres kanoniczny wskazujący na siebie, otrzymuje zamiast tego punktowany noindex_warning.
missing_h1noticepageBrak nagłówka <h1>.
multiple_h1noticepagecountWięcej niż jeden <h1> (informacja).
missing_image_altwarningpagecount, total, sampleObrazy treści bez atrybutu alt. Jawne alt="" oznacza obraz dekoracyjny i nie jest zgłaszane.
thin_contentnoticepageword_count, threshold, segmenterTreść poniżej skonfigurowanej liczby słów. Liczy je tokenizer listy kontrolnej: białe znaki dla systemów pisma ze spacjami, segmentacja słownikowa ICU (segmenter: intl, wymaga ext-intl) dla chińskiego, japońskiego i tajskiego. Japoński artykuł z 400 słowami nie jest więc jednym „słowem”.
mixed_contentwarningpagecount, sampleZasoby podrzędne http:// na stronie https.
html_lang_missingnoticepageBrak <html lang> lub pusta wartość. Technologie asystujące mogą wybrać niewłaściwy głos.
html_lang_invalidnoticepagedeclaredWartość lang nie jest znacznikiem BCP-47 (english, en_US z podkreśleniem, jp).
html_lang_mismatchwarningpagedeclared, declared_script, detected_scriptWidoczna treść jest zapisana innym systemem pisma niż zadeklarowany język: lang="en" na stronie japońskiej, lang="ru" przy tekście łacińskim. Kontrola działa tylko na poziomie systemu pisma; wskazanie niewłaściwego języka zapisanego tym samym alfabetem łacińskim byłoby zgadywaniem, czego skaner nie robi. Wymaga ≥ 40 liter treści.

Kody sieciowe

Wykrywane przez UrlScanner tylko po włączeniu odpowiedniej opcji: seo-pro.scan.url_checks.check_canonical_target dla adresu kanonicznego, check_hreflang_reciprocity dla wariantów hreflang. Każdy cel jest pobierany przez SsrfGuard (dozwolone schematy, zakres hostów, odrzucanie prywatnych IP, budżety przekierowań/czasu/rozmiaru), bez podążania za przekierowaniami, aby przekierowujący adres kanoniczny był widoczny. Adres kanoniczny lub wariant wskazujący na samą stronę jest pomijany, bo właśnie ją pobrano.

KodWażnośćPoleDowodyZnaczenie
blocked_urlnoticepagereasonCel został odrzucony przez SsrfGuard przed jakimkolwiek żądaniem HTTP.
canonical_target_brokencriticalcanonicalcanonical, statusAdres kanoniczny wskazuje stronę zwracającą błąd HTTP.
canonical_target_redirectwarningcanonicalcanonical, status, locationAdres kanoniczny wskazuje stronę z przekierowaniem; ustaw końcowy URL.
canonical_target_noindexwarningcanonicalcanonicalAdres kanoniczny wskazuje stronę, która sama jest noindex.
canonical_target_blockednoticecanonicalcanonical, reasonNie udało się zweryfikować kanonicznego celu (odmowa zabezpieczenia / brak możliwości osiągnięcia).
hreflang_not_reciprocalwarningalternateshreflang, href, statusZadeklarowany wariant nie deklaruje odwołania zwrotnego do strony. Para hreflang może zostać pominięta; samo to nie wyklucza tłumaczenia z indeksowania.
hreflang_target_unverifiednoticealternateshreflang, href, reasonNie udało się pobrać wariantu (odmowa zabezpieczenia, status błędu, przekierowanie, przekroczenie limitu rozmiaru), więc wzajemność nie została sprawdzona. To brak dowodów, a nie defekt.

Kontrola wzajemności pobiera najwyżej hreflang_max_alternates celów na stronę (domyślnie 10), w tym x-default, pomijając duplikaty i samą stronę. Powyższe kody metadanych hreflang_* na poziomie modelu sprawdzają zadeklarowaną listę; ta kontrola jako jedyna potrzebuje drugiej strony.

Każda ścieżka sieciowa tutaj korzysta ze wspólnego SsrfGuard. Model zagrożeń i uwagę o pozostałym ryzyku TOCTOU opisuje SECURITY.md.

Jak kody wpływają na ocenę

Ocena SEO Pro to 100 − stała kara za każdy punktowany problem, ważona według powyższych poziomów. Większość kodów jest uwzględniana; kilka wyłączono celowo: missing_focus_keyword (doradczy), noindex_page i multiple_h1 (informacyjne), blocked_url / canonical_target_blocked / hreflang_target_unverified („nie udało się sprawdzić” ≠ defekt) oraz kody hreflang_*, html_lang_* i aeo_* (sygnały doradcze obecnie nieuwzględniane w ocenie). Strona oceny zawiera pełną listę uwzględnianych kodów i karę dla każdego z nich.

Cykl życia problemu

Problem nie jest tylko wierszem istniejącym, dopóki występuje defekt. Ma cykl życia, a skanowanie uzgadnia problemy celu zamiast je kasować i tworzyć od nowa. Każdy problem ma stabilną tożsamość: cel (scannable_type + scannable_id dla modelu lub url dla celu trasy/mapy witryny) oraz issue_type. Każdy kod jest emitowany najwyżej raz na cel na skanowanie. Kody obejmujące wiele naruszeń (missing_image_alt, mixed_content, hreflang_*, …) łączą je w jeden wiersz z count / sample, dzięki czemu tożsamość jest unikalna.

W każdym skanowaniu dla każdego celu:

  • wynik bez istniejącego wiersza jest tworzony jako open z ustawieniem detected_at;
  • wynik pasujący do otwartego wiersza odświeża dowody i zachowuje pierwotne detected_at — stały moment pierwszego wykrycia, już niezerowany przy każdym skanowaniu;
  • otwarty problem, którego zakończona kontrola już nie wykrywa, otrzymuje stan fixed i czas resolved_at. Wiersz jest zachowany, nie usunięty, więc rzeczywista poprawka zostaje zapisana;
  • problem fixed, który wraca, jest ponownie otwierany w tym samym wierszu (regresja), z ponownym ustawieniem detected_at;
  • problem oznaczony przez użytkownika w panelu jako ignored pozostaje nietknięty.
StanZnaczenieUstawiany przez
openObecnie występuje.skanowanie (nowy lub nadal wykrywany)
fixedWystępował, ale nie jest już wykrywany.skanowanie automatycznie, przy następnym przebiegu, który go nie wykryje
ignoredWyciszony przez użytkownika; wyłączony z liczby otwartych problemów i oceny.akcję Ignoruj w panelu

Ponieważ poprawki są teraz zapisywane zamiast odrzucane, raport pod własną marką może pokazywać rzeczywiste liczby naprawionych i nowych problemów w okresie zamiast różnicy migawek raportów. Odbiorcy liczby otwartych problemów — panel, polecenie seo-pro:scan-status i ocena — filtrują do open, więc zachowane wiersze fixed nigdy nie zawyżają wyników. Naprawione wiersze są przypisane do przebiegu, który je rozwiązał, i usuwane według zwykłego okresu retencji przebiegów skanowania.

Konfiguracja

php
// config/seo-pro.php → 'scan'
'url_checks' => [
    'enabled' => true,
    'crawl_external' => false,             // fetch external URL targets (guarded)
    'check_canonical_target' => false,     // EXEC_NETWORK canonical validation (guarded)
    'check_hreflang_reciprocity' => false, // EXEC_NETWORK hreflang link-back crawl (guarded)
    'hreflang_max_alternates' => 10,       // targets fetched per page by that crawl
],
'checks' => [
    'length' => true,            // title/description length (metadata + rendered)
    'rendered_content' => true,  // H1 / alt / thin content / mixed content / html lang
],
'content' => [
    'min_word_count' => 200,     // thin_content threshold
    'evidence_sample' => 5,      // max example URLs stored per issue
],

Budżet rozmiaru odpowiedzi dla pobrań objętych zabezpieczeniami wynosi seo-pro.http.max_response_bytes (domyślnie 2 MB). Skanowanie tego samego hosta w procesie aplikacji nie ma tego limitu.

Uwaga o zgodności (zmiana nazwy kodu problemu)

Poprzedni pojedynczy kod robots_conflict, który miał dwa poziomy ważności, został rozdzielony, aby każdy kod odpowiadał dokładnie jednemu poziomowi:

Stary kodNowy kodWażność
robots_conflict (index + noindex)robots_conflict_indexingcritical
robots_conflict (follow + nofollow)robots_conflict_followingwarning

Jeśli zapisywałeś lub filtrowałeś po robots_conflict, przejdź na dwa nowe kody.

rankbeam/laravel-seo jest udostępniany na licencji MIT.