Pagina 404: ecran propriu al magazinului și status real pentru adrese inexistente
O adresă greșită pe hyperice.md nu ajunge pe magazin, ci pe pagina implicită a serverului web: un ecran alb, în engleză, cu numele și versiunea serverului pe el și fără niciun drum înapoi spre magazin. Separat, adresele de forma /products/<handle inexistent>, /collections/<slug inexistent> și /blogs/hyperhub/<slug inexistent> răspund cu status 200 deși afișează textul „Pagina nu a fost găsită" — soft-404, interzis explicit de checklistul de lansare. Cele două se repară împreună și în această ordine: fără ecranul propriu de 404, statusul corectat ar duce vizitatorul tot pe pagina serverului.
Ce am găsit
Adresa necunoscută întoarce pagina implicită nginx, nu ecranul magazinului
curl -s -o /tmp/x404.html -w "status=%{http_code} size=%{size_download}" https://hyperice.md/pagina-inexistenta-xyz
status=404 size=153
Corpul celor 153 de octeți:
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.27.5</center>
</body>
</html>
Identic pe /en/nonexistent-xyz și /ru/nesushchestvuyushchaya-xyz (ambele 404, 153 de octeți).
Cauza, la linie în frontend/nginx.conf: 179: return 404;, în interiorul 169: location @spa {. Aceeași locație declară 202: error_page 500 502 503 504 = @spa_shell;, iar regula care ar trebui să servească shell-ul aplicației stă la nivel de server, 210: error_page 404 /index.html;. În nginx error_page se moștenește prin înlocuire, nu prin adăugare: prezența unei directive error_page la nivelul curent anulează complet setul de la nivelul superior. Prin urmare regula de la 210 nu se aplică niciodată în location @spa, iar 404-ul cade pe pagina implicită. Comentariul de la frontend/nginx.conf:176 („error_page below turns this into the app's own 404 screen") descrie intenția, nu comportamentul real.
Ecranul propriu există deja și e gata de folosit: frontend/src/pages/NotFound.tsx:8 randează <Seo page="notFound" noindex />.
Versiunea serverului web e afișată public
grep -n server_tokens frontend/nginx.conf nu întoarce nicio linie, deci valoarea implicită rămâne activă și nginx/1.27.5 apare în corpul fiecărei pagini de eroare implicite. Antetul server: vine de la Cloudflare, deci versiunea scapă doar prin corpul paginii de eroare — motiv în plus să nu mai fie servit. Nu e o breșă, dar spune oricui caută ținte exact ce versiune să verifice în listele de vulnerabilități.
Produs, colecție sau articol inexistent răspunde cu 200 în loc de 404
Statusuri cerute pe producție:
/aceasta-nu-exista 404
/products/nu-exista 200
/collections/nu-exista 200
/blogs/hyperhub/nu-exista 200
/en/products/nu-exista 200
/ru/collections/nu-exista 200
Și pe un handle plauzibil, care chiar circulă ca link: curl -sS -o /dev/null -w "%{http_code}" https://hyperice.md/products/hypervolt-2-pro → 200, cu <title>Pagina nu a fost găsită | Hyperice Moldova</title> și <meta name="robots" content="noindex, follow" /> în corp. Mărimile corpului: 4204 octeți pe /products/nu-exista-xyz, 4222 pe /collections/nu-exista-xyz, 4240 pe /blogs/hyperhub/nu-exista-xyz.
În cod: backend/src/services/page-meta.ts:151 — funcția notFound() setează doar noindex: true (linia 157), fără niciun status; backend/src/services/render-page.ts:119-120 transformă acel noindex în eticheta robots. Ruta backend/src/routes/render.ts:42 se încheie cu res.send(html), fără niciun res.status(...), deci răspunsul e mereu 200. Harta $spa_known din frontend/nginx.conf:71-91 lasă ~^(/(?:ru|en))?/products/[^/]+/?$ (linia 76) să treacă la randor, pentru că nginx nu poate ști ce handle-uri există în baza de date.
Capcana de care depinde fixul: location @spa are proxy_intercept_errors on (frontend/nginx.conf:199). Un 404 venit de la /api/render ar fi interceptat și înlocuit, deci s-ar pierde tocmai HTML-ul randat. Statusul trebuie corectat și interceptarea ajustată în același pas.
Efectul pentru magazin: noindex, follow e servit efectiv pe fiecare astfel de pagină, deci adresele moarte nu intră în Google. Rămâne însă că orice unealtă care verifică linkuri — Search Console, verificatoarele de linkuri moarte, un partener care ne pune link — vede „pagină funcțională" acolo unde e o pagină moartă, așa că un link greșit nu iese niciodată la iveală.
De făcut
-
În frontend/nginx.conf, în interiorul bloculuilocation @spa(liniile 169-203), adaugăerror_page 404 /index.html;lângă linia 202, fără=, astfel încât statusul 404 să se păstreze și doar corpul să fie înlocuit cu shell-ul aplicației. Directiva trebuie să stea în aceeași locație, nu la nivel de server, din cauza regulii de moștenire prin înlocuire. -
Marchează pagina de negăsit cu un status explicit: un câmp status: 404pePageMeta, setat doar înnotFound()labackend/src/services/page-meta.ts:151. -
Propagă statusul prin renderPageși trimiteres.status(404)înainte deres.send(html)înbackend/src/routes/render.ts. -
Ajustează proxy_intercept_errorsdinfrontend/nginx.conf:199astfel încât 404-ul venit de la/api/rendersă ajungă la vizitator împreună cu HTML-ul randat, nu înlocuit de shell-ul gol. -
Adaugă server_tokens off;în bloculhttp/serverdinfrontend/nginx.conf. -
Testează întreg lanțul pe dev înainte de producție — modificarea atinge pista de producție.
Criteriu de acceptare
-
curl -s -D- https://hyperice.md/pagina-inexistenta-xyzîntoarce în continuareHTTP/2 404, dar corpul conține<div id="root">și<meta name="robots" content="noindex, follow">, nu cei 153 de octeți ai serverului. Același rezultat pe/en/...și/ru/.... - Corpul paginii de eroare nu mai conține numărul de versiune al serverului.
-
curl -s -o /dev/null -w "%{http_code}" https://hyperice.md/products/nu-exista→404, iar corpul rămâne ecranul de negăsit al magazinului. Idem pentru/collections/nu-exista,/blogs/hyperhub/nu-existași variantele/enși/ru. - Un produs real (
/products/hypervolt-3,/products/arm-attachment) rămâne200, cu toate meta-urile intacte — verificat pe minimum 5 handle-uri publicate. - O pagină reală (
/about) rămâne200și randată complet.