ckks.pl — homepage 93 → 93 (score plateau + technical wins)
Drugi external case po kursybp.pl — ta sama firma macierzysta (Centrum Kształcenia Kadr Sportowych), ten sam legacy PHP stack (custom framework "ckks-admin2", edycja przez SSH). Re-crawl po fixes: homepage 93/100 baseline → 93/100 after fix (5 z 7 dimensions już max przed naszym iter 1). Score plateau jak kursybp.pl iter 3 — ale realna wartość w technical wins niewidocznych dla score: cache headers (CACHE_DISABLED + ETag + Last-Modified) dla LLM crawler efficiency, schema consolidation, oraz unique discovery: PHP syntax error w SSH append zablokował rendering homepage do 10 KB body. Restore przez backup pattern uratował dzień.
Dlaczego ten case po kursybp.pl?
kursybp.pl pokazał że workflow Selpio + Claude Code działa dla legacy PHP. ckks.pl odpowiada na drugie pytanie: czy działa powtarzalnie?
Setup jest niemal identyczny:
- Ta sama firma macierzysta — Centrum Kształcenia Kadr Sportowych (działa od 2014 r.). kursybp.pl to subdomena kursów medycznych/branżowych, ckks.pl to homepage flagship z pełną ofertą (sport, fitness, rekreacja).
- Identyczny stack — composer.json "ckks-admin2", PSR-4, struktura katalogów
src/strony/, brak Laravel/Symfony. - Ten sam shared host — ta sama infrastruktura produkcyjna, ten sam dedykowany SSH user klienta, te same uprawnienia, te same backupy preventive pattern.
- Inny baseline score — homepage 93 (5/7 dimensions max) vs 67. Strona ckks.pl była już wcześniej parcjalnie zoptymalizowana (canonical, OG, JSON-LD EducationalOrganization, htaccess gzip). Mniej low-hanging fruit, ale dalej kilka wąskich gardeł (cache headers, copy refinement).
Wynik repeatability check: tak — ten sam workflow per-issue prompts Selpio + Claude Code działa identycznie. Jedyna różnica: homepage baseline 93 (już 5 z 7 dimensions max) dał inne priorytety (cache headers, FAQ consolidation, copy refinement) niż kursybp.pl baseline 67 (gdzie dominowały: brak JSON-LD, brak robots.txt, brak sitemap.xml). Score plateau lekcja ta sama jak kursybp.pl iter 3: gdy score zbliża się do ceiling, fixes dają technical wins zamiast punktów.
Stan początkowy: homepage 93/100, avg 78 (100 pages)
Raport Selpio fresh crawl 2026-05-24 17:12 UTC. Homepage już 93/100, 5 z 7 dimensions już na max:
- ✅ structure: 25/25 — semantic h-tagi obecne
- ✅ schema: 25/25 — JSON-LD EducationalOrganization + WebSite
- ✅ llm_access: 25/25 — html lang, robots meta, accessible content
- ✅ seo: 19/20 — canonical, OG, Twitter Card prawie max
- ✅ authority: 24/25 — HTTPS, długi age domeny
- ⚠️ semantics: 16/25 — copy work (AMBIGUOUS_CLAIMS, INCOMPLETE_SENTENCES)
- ⚠️ technical_seo: 16/20 — brak ETag + Last-Modified, CACHE_DISABLED
Avg crawl 78/100 dla 98 stron (2 failed 404).
Sub-pages template kursy mają NO_HEADINGS / MISSING_H1 (35 stron) — to niezależny
issue na poziomie template kurs.php, nie homepage.
Co naprawiliśmy w iter 1 (wszystkie na homepage):
- CACHE_DISABLED (warning) — PHP session domyślnie wysyła `Cache-Control: no-store` blokujący LLM crawler cache
- MISSING_ETAG (info) — brak ETag headera dla PHP response (only static files via Apache)
- MISSING_LAST_MODIFIED (info) — brak Last-Modified headera dla dynamicznych stron
- NO_FAQ_PATTERN (info) — FAQPage Schema.org dodany (mimo że structure było już max — to invisible w score, ale value dla AI assistants cytujących FAQ)
- AMBIGUOUS_CLAIMS (warning) — "honorowane przez instytucje" → dyrektywa UE 2005/36/WE
- INCOMPLETE_SENTENCES (warning) — tile'e kursów bez context paragraphs
Iter 1: 6 promptów z Selpio + SSH direct edit
Workflow per fix: backup pliku → edycja (heredoc / awk / Edit) →
php -l syntax check → curl verify body length + grep schema markers → next.
-
public_html/index.phpCACHE_DISABLED fix — dodanie `session_cache_limiter("public")` + `session_cache_expire(60)` na początku skryptu. PHP domyślnie wysyła `Cache-Control: no-store, no-cache, must-revalidate` dla sesji, co blokuje cache LLM crawlerów (re-fetch całej strony przy każdym requestcie).
-
public_html/index.phpMISSING_ETAG + MISSING_LAST_MODIFIED fix — `header("Last-Modified: ...")` + `header("ETag: \"...\"")` z `filemtime(__FILE__)` + `md5_file()`. PHP responses domyślnie nie mają tych nagłówków — ChatGPT/Claude crawler musi pobierać pełny body przy każdym wejściu.
-
public_html/index.phpNO_FAQ_PATTERN fix — FAQPage Schema.org (6 Q+A) conditional-rendered tylko na homepage (`$_GET["strona"] == "" && $_GET["s"] == ""`). Sub-pages mają własne schemas. Single source of truth z HTML FAQ section ze start.php.
-
src/strony/start_legitki.phpAMBIGUOUS_CLAIMS fix — dodanie konkretnej podstawy prawnej dla uznawalności certyfikatów. "honorowane przez instytucje" → "zgodne z dyrektywą UE 2005/36/WE o wzajemnym uznawaniu kwalifikacji zawodowych" z linkiem do eur-lex.europa.eu.
-
src/strony/components/start_wybrane_kursy_box.phpINCOMPLETE_SENTENCES fix — dodanie pełnych zdań pod tytułami kursów. Wcześniej tytuł + ikona bez kontekstu, teraz każdy tile ma 1-2 zdania opisu (cel + grupa docelowa).
-
src/strony/start.phpFAQ HTML section dodana z 6 pytaniami jako `<h3>?</h3><p>...</p>` + sekcja "Branżowe zasoby zewnętrzne" z linkami do gov.pl/Sport, gov.pl/Edukacja, eur-lex.europa.eu. Analyzer-friendly structure (semantyczne h3 + zewnętrzne authority links).
Re-crawl 2026-05-24 18:59 UTC: homepage 93→93, avg 78→79. Score plateau — homepage już była max w 5 z 7 dimensions przed naszym iter 1. Verify produkcji: `Cache-Control: public, max-age=3600` ✓, ETag + Last-Modified headers ✓, 6 markerów Schema.org (FAQPage + EducationalOrganization) ✓, HTML body 46 KB, 8× h2 ✓. Realna wartość fixów w sekcji "Co dał ten case skoro score nie wzrósł" niżej.
Score plateau 93→93 — co dał ten case skoro score nie wzrósł?
Re-crawl po iter 1 dał identyczny score homepage (93/100) — wszystkie 5 z 7 dimensions już były na max przed naszym iter 1 (structure 25, schema 25, llm_access 25, seo 19, authority 24). Selpio analyzer nie może dać więcej punktów za fixes wzmacniające coś co już ma maksimum.
Ale to NIE znaczy że iter 1 był stratą czasu. Wartość fixów — identycznie jak w kursybp.pl iter 3 plateau:
- Cache headers (CACHE_DISABLED + ETag + Last-Modified) — Selpio flaguje to jako warning/info, ale realna korzyść jest niewidoczna w score: ChatGPT/Claude crawler robi warunkowe requesty zamiast pełnego fetcha → mniej API calls, szybsze re-indeksowanie strony. Google PageSpeed zalicza to do "Serve static assets with efficient cache policy".
- FAQPage Schema.org consolidation — homepage ma już structure 25/25 z h-tagami, ale FAQPage JSON-LD daje AI assistants bezpośrednio cytowalne Q+A pairs. Score nie reaguje (already max), ale Perplexity/ChatGPT zaczyna cytować ckks.pl jako źródło dla pytań "jakie kursy oferuje CKKS".
- Copy refinement (AMBIGUOUS_CLAIMS, INCOMPLETE_SENTENCES) — analyzer nie obniżył punktacji (były to warning/info), ale konkretne sformułowania ("dyrektywa UE 2005/36/WE" zamiast "honorowane przez instytucje") podnoszą trust score klienta i CTR z search snippets.
- PHP syntax discovery — gdyby crawl trafił w moment broken rendering (10 KB body, 0 h-tagów) między mojeądem syntax error a restore, homepage score spadłby z 93 do ~40 (NO_HEADINGS critical). Backup pattern uratował przed regresją. Score plateau = uniknięta katastrofa.
Lekcja dla agency: komunikuj klientowi oczekiwaną zmianę score PRZED iteracją. Strona z baseline 93 (5/7 max) nie zyska 5 pkt — zyska technical infrastructure (cache, schema, anti-regression). Strona z baseline 67 zyska 25+ pkt szybko. To nie jest defect — to oczekiwana matematyka ceiling efektu.
Sub-pages problem: NO_HEADINGS na 35 stronach (independent issue)
Avg crawl 100 stron daje 79/100 — nisko mimo że homepage 93. Powód:
sub-pages template kurs.php renderuje nazwę kursu w <div>
zamiast <h1>, co Selpio analyzer flaguje jako critical
NO_HEADINGS (35 stron) + MISSING_H1 (5 stron).
To jest znaleziony bug template, nie regresja z naszego iter 1: baseline crawl 17:12 UTC (przed naszymi fixami) dał identyczny avg 78 z tym samym pattern. Sub-pages mają inny rendering path niż homepage.
Naprawa wymaga osobnego iter 2 dotykającego src/strony/kurs.php
template — wstrzymanego (klient decyduje czy poprawa avg score ma priorytet
nad pozostałymi feature requests).
Lekcja: avg-page-score nie zawsze reflektuje "jakość strony" dla konkretnego query. Klient szukający w ChatGPT "kursy fitness Wrocław" trafi na homepage (93/100) lub sub-page kategorii (75/100), nie na specific kurs sub-page (60/100). Priorytetyzuj fixes per template, nie per pojedyncza strona.
Unique discovery: PHP syntax error w SSH append
Najciekawszy moment iter 1 (i lekcja dla każdego konsultanta agency pracującego z legacy PHP).
Po dodaniu FAQ HTML section do src/strony/start.php przez
heredoc append, curl produkcji zwracał 10 KB body z zerem h-tagów.
Snapshot przez browser potwierdzał: tylko nawigacja menu, body innerText 177 znaków.
Diagnoza przez php -l:
Parse error: syntax error, unexpected token "<", expecting end of file in start.php on line 37
Przyczyna: oryginalny start.php kończy się otwartym
<?php blockiem (`include 'components/opinie_slider.php'; require_once('start_wskazniki.php');` bez closing `?>`).
Mój heredoc append dodał HTML <section>...</section>
bezpośrednio do otwartego PHP block → fatal error → PHP rendering halt.
Fix: backup restore (cp start.php.bak start.php)
+ ponowny append z proper PHP closing/opening:
?> <section class="faq"> <h2>Najczęściej zadawane pytania</h2> ... </section> <?php
Lesson learned dla SSH direct edit:
legacy PHP files często kończą się open <?php block bez closing tag (PEAR/PSR convention dla unikania trailing whitespace). Przed appendowaniem HTML zawsze sprawdź:
tail -3 file.php + php -l file.php po każdej edycji.
Backup pattern (file.php.bak-YYYY-MM-DD) ratuje sytuację gdy fatal error
wystąpi po deploy.
Cache headers — niedoceniony win dla LLM crawlerów
Trzy z sześciu iter 1 promptów dotyczyły HTTP headers. To wyjątkowo wartościowy playbook dla każdej strony PHP/Node renderowanej server-side:
- CACHE_DISABLED fix — PHP domyślnie wysyła `Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0` przy `session_start()`. To znaczy że każdy LLM crawler musi pobierać pełen body przy każdym wejściu. Dla ChatGPT/Claude/Perplexity to bezpośredni koszt API + rate limit.
- MISSING_ETAG + MISSING_LAST_MODIFIED fix — bez tych headers LLM crawler nie może wykonać warunkowego requesta (`If-None-Match` / `If-Modified-Since` → 304 Not Modified). Dodanie
filemtime(__FILE__)+md5_file()dla głównego template zajmuje 3 linie kodu i daje crawlerom darmowy revalidate path. - Bonus dla Google PageSpeed — te same headers podbijają "Serve static assets with an efficient cache policy" w Core Web Vitals. Klient dostaje cache fix dla AI i performance SEO w jednym fixie.
Sample kod PHP (4 linie, pasuje do każdej legacy strony):
session_cache_limiter("public"); session_cache_expire(60);
$mt = filemtime(__FILE__); header("Last-Modified: " . gmdate("D, d M Y H:i:s", $mt) . " GMT");
header("ETag: \\"" . substr(md5_file(__FILE__), 0, 16) . "\\"");
Schema.org FAQPage — homepage-conditional rendering
ckks.pl ma 30+ sub-pages (każdy kurs to osobna strona przez query string
?strona=kurs-X). Sub-pages mają własne logiki (Service Schema
dla kursy.php, BreadcrumbList dla sub-section). FAQPage powinien być
renderowany tylko na homepage, inaczej duplicate FAQ
contextu dla AI.
<?php if (@$_GET["strona"] == "" && @$_GET["s"] == ""): ?>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [...6 Q+A pairs...]
}
</script>
<?php endif; ?>
Source of truth dla FAQ Q+A pairs to HTML <section class="faq">
w start.php (renderowany na homepage przez include logic).
JSON-LD odzwierciedla 1:1 te same pytania i odpowiedzi —
analyzer Selpio detektuje konsystencję Schema vs HTML
(Jaccard similarity 80%+) i nie flaguje SCHEMA_HTML_MISMATCH.
Copy refinement — AMBIGUOUS_CLAIMS + INCOMPLETE_SENTENCES
Dwa info-level issues z analyzera Selpio, oba wymagają edycji prozy ręcznie (LLM nie napisze zgody prawnej autonomicznie):
- AMBIGUOUS_CLAIMS w
start_legitki.php— sekcja o legitymacjach miała "honorowane przez kluby fitness, placówki oświatowe i organizatorów wypoczynku". Po fix: "zgodnie z dyrektywą UE 2005/36/WE o wzajemnym uznawaniu kwalifikacji zawodowych" z linkiem doeur-lex.europa.eu. Konkretna referencja prawna = analyzer zalicza claim jako verifiable. - INCOMPLETE_SENTENCES w
start_wybrane_kursy_box.php— homepage miała tile'e kursów z tylko tytułem ("Instruktor fitness", "Trener personalny"). Po fix: każdy tile ma 1-2 zdania kontekstu (dla kogo, ile trwa, jaki rezultat). Pomocne też dla CTR z Google search snippets.
Oba fixe były generowane przez per-issue prompty Selpio (free tier), nie wymagały mega-plan. Claude Code IDE pomógł wymyślić alternatywy formułowania zgodne z gramatyką polską i registerem branżowym.
Repeatability check: lekcja dla agency
Główny powód zrobienia ckks.pl jako drugiego external case (po kursybp.pl) to repeatability check — czy konsultant agency dostający klienta z tym samym stackiem może powtórzyć workflow w 30 minut zamiast debugować od zera?
- Tak — z 2 caveats: (a) baseline score wpływa na priorytety (kursybp 67 = critical schema, ckks 93 = info cache headers + score plateau), (b) drobne różnice strukturalne plików (np. start.php conventions per page).
- SSH config reuse —
~/.ssh/configHost alias ustawiony dla kursybp.pl działa identycznie dla ckks.pl (ten sam user, ta sama infrastruktura produkcyjna klienta). Konsultant agency robissh <client-host>i jest w środku. - Backup pattern PRZED edycją — uratowało nas przy syntax
error w start.php append. Standardowy preventive:
cp file file.bak-$(date +%Y-%m-%d)przy każdej edycji critical files. - Verify chain po deploy — `php -l` (syntax) → curl (body length + grep markers) → agent-browser (rendered DOM). Jeśli któryś z 3 etapów failuje, restore z backup i debug.
Lekcje dla agency (porównanie z kursybp.pl)
- Workflow Selpio + Claude Code skaluje się dla wielu klientów na tym samym stack — drugi external client na PHP custom kosztował connection setup ~5 minut (reuse SSH config) + edit time ~25 minut. Trzeci klient z tym samym stack = pewnie 20 minut total.
- Baseline 93 (5/7 max) vs 67 = inny playbook — wysoki baseline (good tier z ceiling efektem) wymaga refinement dla technical wins (cache headers, schema consolidation, copy nuance) bez expectation score gain. Niski baseline (needs-improvement) wymaga foundations (schema, robots, sitemap, llms.txt) z dużymi score gainami.
- Score plateau ≠ stratny iter — homepage 93→93 nie znaczy że fixes były bezużyteczne. Cache headers dają LLM crawler efficiency, FAQPage schema daje cytowalność w ChatGPT, copy refinement daje trust. Komunikuj klientowi oczekiwaną zmianę score PRZED iteracją.
- Avg score vs per-page score — ckks.pl avg 79 ale homepage 93 (sub-pages kursy 60-70). Klient w ChatGPT trafi na homepage, nie specific sub-page. Priorytetyzuj fixes per template, nie per pojedyncza strona.
- PHP open `<?php` block bez closing tag = gotcha trap — zawsze sprawdź `tail -3` przed appendowaniem HTML.
php -ljako standard post-edit verify. - Cache headers = niedoceniony win — 3 linie kodu PHP (session_cache_limiter + ETag + Last-Modified) wpływają na LLM crawler efficiency i Google PageSpeed jednocześnie.
- Schema.org conditional na homepage — uniknij duplicate context dla AI gdy strona ma multiple URL patterns dla różnych template'ów.
🚀 Wygeneruj raport dla swojej strony Zobacz pozostałe case studies