Strony firmowe, landing page i serwisy w WordPress oraz Next.js — projektowane pod konwersję i widoczność w Google.
Tworzenie stron WWWProjektowanie UX/UIStrony dla firmLanding pageRedesign strony
Zbuduj mapę starych i nowych adresów, wybierz właściwe reguły Apache i sprawdź każdy kierunek przekierowania. Z kodem, testami negatywnymi, wynikami lokalnego laboratorium oraz procedurą bezpiecznego wycofania zmiany.
Przekierowanie 301 informuje, że adres został przeniesiony na stałe. Poprawne wdrożenie to jednak nie tylko odpowiedź 301: stary URL powinien prowadzić do właściwego odpowiednika, bez pętli i zbędnych skoków, a końcowy adres musi działać. W tym tutorialu przechodzimy od mapy URL do testu HTTP, wdrożenia i rollbacku.
Jeżeli potrzebujesz jednego przekierowania, zacznij tutaj. Wariant wymaga potwierdzonego Apache 2.4 z mod_rewrite i obsługą .htaccess w katalogu głównym witryny. Jeśli masz tylko panel przekierowań hostingu, użyj instrukcji operatora dla tego panelu; nie dodawaj równolegle drugiej reguły w pliku. Brak dostępu do plików lub niepewność co do serwera oznacza, że ten fragment należy przekazać administratorowi, a nie wklejać w edytor treści WordPressa.
RewriteEngine On
RewriteRule ^stara-oferta/?$ https://sklep.example/oferta.html [R=301,END]Ten wariant zachowuje przychodzące parametry, w tym UTM. Nie obejmuje całej domeny ani /stara-oferta/podstrona. Jeśli migrujesz wiele adresów, identyfikatory typu ?p=123 lub zmieniasz politykę parametrów, przejdź przez mapę i testy poniżej. Nie rozszerzaj regexu na całą witrynę bez takiej mapy.
ZIP edukacyjny. Test WordPress wymaga osobnego, już przygotowanego lokalnego laboratorium WP. Pakiet zawiera także celowo błędne scenariusze bad-*. Nie wgrywaj go na hosting produkcyjny.
Ten kod dotyczy Apache 2.4 i pliku .htaccess w katalogu głównym witryny, czyli DocumentRoot. Nginx nie odczytuje .htaccess. Zgodności konkretnej instalacji LiteSpeed nie zakładamy na podstawie testu Apache. Zanim zmienisz plik, potwierdź serwer i warstwę obsługującą dany URL w panelu hostingu lub u administratora. Sam nagłówek Server może pochodzić z proxy i nie rozstrzyga tej kwestii.
Apache opisuje kontekst i ograniczenia pliku w dokumentacji .htaccess. W naszym laboratorium serwer dopuszczał tylko konkretne dyrektywy przekierowań, bez zmiany ustawień systemowego Apache.
Połącz adresy z istniejącej sitemap, crawla, stron docelowych w GSC i analityce oraz istotnych linków przychodzących. Przechowaj źródło informacji i decyzję właściciela treści. Brak kliknięć w krótkim okresie nie oznacza automatycznie, że URL można usunąć. Mapa ma wyjaśniać, który nowy zasób faktycznie zastępuje stary; podobieństwo nazwy sluga nie wystarcza.
| Stary URL / wariant | Decyzja | Oczekiwana odpowiedź |
|---|---|---|
| /stara-oferta i /stara-oferta/ | Ta sama oferta pod /oferta.html | 301 → /oferta.html → 200, jeden skok |
| /stara-oferta?utm_source=test | Zachować parametr pomiarowy | 301 → /oferta.html?utm_source=test |
| /index.php?p=123 | Konkretny identyfikator poradnika | 301 → /poradnik.html, bez p=123 |
| /stara-kampania?utm_source=test | W tym przykładzie świadomie usunąć stare parametry | 301 → /oferta.html, bez query string |
| /wycofane-bez-zamiennika | Usunięty zasób bez odpowiednika | 410, bez Location |
| /nie-istnieje | Nieznany adres | 404, bez Location |
| /oferta.html | Aktualny adres końcowy | 200, bez przekierowania |
Dla każdego wiersza dopisz warianty HTTP/HTTPS i www/bez www, jeśli są obsługiwane, politykę parametrów, odbiorcę treści oraz kryterium sukcesu. W pliku testowym zachowuj wielkość liter i kodowanie ścieżek. Część po #, np. #cennik, nie jest wysyłana do serwera i nie może stanowić warunku RewriteRule. Nazwę pliku z kropką trzeba potraktować jako dokładny tekst, a nie dowolny znak regex.
Zasady mapowania, unikania nietrafnych przekierowań i aktualizacji sygnałów opisuje Google: migracja witryny ze zmianą URL.
| Sytuacja | Wybór | Co sprawdzić |
|---|---|---|
| Stała zmiana adresu dokumentu dostępnego przez GET | 301 lub 308 | Zgodność treści, finalne 200 i jeden skok |
| Stałe przeniesienie endpointu z zachowaniem metody i body | 308, po weryfikacji kontraktu API | Obsługę klienta, autoryzację i bezpieczeństwo ponowienia żądania |
| Zmiana tylko tymczasowa | Rozważ 302 lub 307, nie utrwalaj jej jako 301 | Czas obowiązywania oraz wymóg zachowania metody |
| Usunięcie bez trafnego odpowiednika | 404 lub 410 | Czy zasób naprawdę usunięto; brak catch-all na homepage |
Google traktuje zarówno 301, jak i 308 jako przekierowania stałe i sygnał wyboru adresu kanonicznego. To nie gwarancja pozycji, indeksacji ani zachowania konkretnej liczby kliknięć. Źródło: Google Search Central: przekierowania i wyszukiwarka.
W eksperymencie curl po 301 zmienił POST na GET i nie przesłał body; po 308 zachował POST oraz syntetyczne body. Nie używaliśmy opcji -X POST ani --post301, które zmieniłyby warunki testu. HTTP dopuszcza zmianę POST na GET przy 301, natomiast 308 zachowuje metodę. Sprawdź RFC 9110: 301 i 308. Nie testuj tego przez wysyłanie zamówień lub prawdziwych formularzy na produkcji.
Dla prostych map bez warunków query string wystarcza mod_alias. Uważaj jednak: Redirect operuje na prefiksie pełnych segmentów ścieżki. Reguła dla /alias-prefix obejmuje też /alias-prefix/child i dołącza końcówkę do celu. Nie obejmuje /alias-prefix-other. Jeśli chcesz pojedynczy adres, użyj zakotwiczonego RedirectMatch albo dokładnego RewriteRule.
# Prefiks: obejmuje także /alias-prefix/child.
Redirect 301 /alias-prefix https://sklep.example/nowy-prefix
# Dokładna ścieżka, z opcjonalnym końcowym ukośnikiem.
RedirectMatch 301 ^/alias-exact/?$ https://sklep.example/oferta.htmlW obrębie tej samej konfiguracji mod_alias pierwsze pasujące przekierowanie ma pierwszeństwo, więc wyjątki zapisuj przed szerokimi regułami. Nie zakładaj jednak, że samo przeniesienie linii mod_alias nad mod_rewrite rozstrzygnie kolejność między modułami. Dla tej samej grupy adresów stosuj jeden mechanizm i sprawdź rzeczywistą odpowiedź. Źródło: Apache mod_alias: kolejność, Redirect i RedirectMatch.
Poniższy blok jest fragmentem reguł, nie zamiennikiem całego pliku .htaccess. Domena sklep.example jest celowo przykładowa. Wstaw własny, stały adres kanoniczny oraz zatwierdzoną mapę. Zachowaj istniejące zabezpieczenia i konfigurację hostingu. W typowym układzie z front controllerem własne przekierowania muszą zostać rozpatrzone przed wewnętrznym przepisaniem do aplikacji; ustal to w swojej konfiguracji i nie edytuj sekcji automatycznie nadpisywanej przez CMS.
# Apache 2.4, .htaccess w DocumentRoot, stała domena bez końcowego /.
# Podstaw WŁASNĄ domenę i ZATWIERDZONĄ mapę. To nie cały plik CMS-a.
RewriteEngine On
RewriteCond %{QUERY_STRING} ^p=123$
RewriteRule ^index\.php$ https://sklep.example/poradnik.html [R=301,END,QSD]
RewriteRule ^stara-oferta/?$ https://sklep.example/oferta.html [R=301,END]
RewriteRule ^stary-poradnik\.html$ https://sklep.example/poradnik.html [R=301,END]
RewriteRule ^stare-zażółć$ https://sklep.example/poradnik.html [R=301,END]
RewriteRule ^stara-kampania$ https://sklep.example/oferta.html [R=301,END,QSD]
RewriteRule ^stary-filtr$ https://sklep.example/oferta.html?kolor=czarny [R=301,END]
RewriteRule ^stary-filtr-qsa$ https://sklep.example/oferta.html?kolor=czarny [R=301,END,QSA]
# Tylko ten konkretny zasób usunięto bez odpowiednika.
RewriteRule ^wycofane-bez-zamiennika$ - [G,END]| Reguła | Zachowanie | Wynik dla parametrów testowych |
|---|---|---|
| Cel bez ? i bez QSD | Kopiuje przychodzący query string | /stara-oferta?utm_source=test → /oferta.html?utm_source=test |
| Cel bez ? z QSD | Usuwa cały przychodzący query string | /stara-kampania?utm_source=test → /oferta.html |
| Cel z własnym ? bez QSA | Zastępuje przychodzący query string | /stary-filtr?utm_source=test → /oferta.html?kolor=czarny |
| Cel z własnym ? oraz QSA | Dołącza parametry wejściowe do docelowych | /stary-filtr-qsa?utm_source=test → /oferta.html?kolor=czarny&utm_source=test |
QSD usuwa także parametry mające znaczenie dla aplikacji lub analityki: nie dodawaj go globalnie. QSA może z kolei utworzyć powtarzające się klucze, których interpretacja zależy od aplikacji. Testuj osobno parametry kampanii, identyfikatory treści i filtry. Nie przenoś tokenów logowania do nowego publicznego URL-a. W przykładzie z polskimi znakami żądanie zawierało kodowanie procentowe, a wzorzec dopasował zdekodowaną ścieżkę; dowolne backreference i złożone kodowanie wymagają odrębnych testów.
Podstawy: Apache mod_rewrite: kontekst katalogowy i zmienne oraz flagi R, END, QSD i QSA.
# Terminal na Twoim komputerze. Podstaw własny publiczny URL HTTPS.
redirect_url='https://sklep.example/stara-oferta?utm_source=test'
# 1. Rzeczywisty GET: status i Location pierwszej odpowiedzi, bez podążania.
curl --silent --show-error --globoff --connect-timeout 5 --max-time 15 \
--proto '=https' --dump-header - --output /dev/null "$redirect_url"
# 2. Dopiero po sprawdzeniu domeny w Location: docelowy status i liczba skoków.
curl --silent --show-error --globoff --location --max-redirs 5 \
--connect-timeout 5 --max-time 20 \
--proto '=https' --proto-redir '=https' --output /dev/null \
--write-out 'status=%{http_code}\nredirects=%{num_redirects}\nurl=%{url_effective}\n' \
"$redirect_url"Nie zastępuj pierwszego testu samym curl -I: to metoda HEAD, której obsługa może różnić się od GET. Nie dodajemy --fail, bo 404 i 410 są poprawnymi oczekiwanymi wynikami niektórych wierszy mapy. Dla pętli curl z ograniczeniem liczby przekierowań zakończy się błędem; w naszym teście było to wyjście 47 po trzech skokach. Nie usuwaj limitu, aby wymusić zielony wynik.
Przy większej mapie użyj check-map.py z paczki. Dla każdego źródła sprawdza oczekiwany status i dokładny Location, a potem pobiera osobno zatwierdzony cel. Jeśli cel również przekierowuje, test nie przechodzi. Skrypt nie podąża za niesprawdzonym adresem z nagłówka Location i nie wysyła danych autoryzacji. Wersja z paczki dotyczy mapy w obrębie jednego originu, nie migracji między domenami.
[
{"source":"/stara-oferta","status":301,"target":"/oferta.html"},
{"source":"/stara-oferta?utm_source=test","status":301,"target":"/oferta.html?utm_source=test"},
{"source":"/oferta.html","status":200},
{"source":"/stara-oferta-dodatkowa","status":404}
]python3 check-map.py --origin 'https://sklep.example' --map moja-mapa.json --delay 1python3 check-map.py --origin 'https://sklep.example' --map moja-mapa.json --dry-runDry-run odrzuca brak celu, powtórzone źródła (również identyczne), przekierowanie na siebie, cykl A → B → A, łańcuch do kolejnego przekierowania oraz cel, który ta sama mapa opisuje jako 404/410. Walidacja działa też przed każdym testem HTTP. Dokładne warianty query string są oddzielnymi wierszami; automat nie zgaduje równoważności parametrów, kodowania, wielkości liter lub treści. Cel niewystępujący jako źródło w mapie wymaga późniejszego GET — samo poprawne parsowanie JSON nie potwierdza jego istnienia.
Rozpakuj pobrany ZIP do nowego katalogu na własnym komputerze, poza katalogiem jakiejkolwiek strony. Przejrzyj README i kod. Na macOS z natywnym /usr/sbin/httpd, dostępnymi modułami Apache, Pythonem 3 i curl uruchom poniższe polecenie jako zwykły użytkownik. Nie używaj sudo. Skrypt tworzy własny katalog tymczasowy, uruchamia odseparowany Apache na losowym wysokim porcie 127.0.0.1 i po teście zatrzymuje wyłącznie własny proces.
python3 run-lab.pypython3 test-seo-checks.py| Test | Zaobserwowano | Znaczenie |
|---|---|---|
| Dokładna mapa + parametry | 19 pozytywnych i negatywnych wierszy PASS | Ukośniki, litery, kodowanie, query, 200/301/404/410 |
| Stary URL → właściwy cel | 200, num_redirects=1 | Brak dodatkowego skoku |
| Podmieniony Host i parametr next | Stały lokalny origin w Location | Te konkretne reguły nie przekazały sterowania na obcy host |
| POST z body synthetic=alpha | 301: GET i puste body; 308: POST i zachowane body | Zachowanie curl 8.7.1 w opisanym trybie |
| Szeroka reguła przed wyjątkiem | Przekierowanie do nieprawidłowej oferty | Odtworzony błąd kolejności, nie dobry wynik biznesowy |
| A → B → oferta | 200, num_redirects=2; checker odrzucił mapę | Samo finalne 200 nie wykrywa łańcucha |
| A → B → A, również z END | curl exit=47 po 3 skokach | Pętla zewnętrzna została wykryta i zatrzymana limitem |
| Błędna dyrektywa w .htaccess | httpd -t: sukces; rzeczywisty GET: 500 | Test konfiguracji głównej nie wystarczył |
| Przywrócenie poprzedniego .htaccess | Stary URL ponownie 404; aktualna oferta nadal 200 | Rollback przywrócił baseline tego laboratorium |
PASS przy scenariuszu bad-* oznacza, że test poprawnie odtworzył i wykrył oczekiwany błąd. Nie oznacza dopuszczenia tej konfiguracji do publikacji. Nie uruchamiaj całej paczki na serwerze swojej strony. Laboratoryjny HTTP nie zastępuje testu certyfikatu, CDN ani wariantów produkcyjnej domeny.
Na WordPress 7.1 z Apache 2.4.68 wykonaliśmy dodatkową próbę przez lokalną bramę Nginx. Utworzyliśmy jedną własną stronę testową i sprawdziliśmy jej treść oraz canonical. Reguła umieszczona przed istniejącym front controllerem WordPressa zwracała 301 → właściwa strona 200 dla starego URL-a, końcowego ukośnika i parametru UTM. Sąsiedni, nieobjęty wzorcem adres pozostał 404. Ta sama reguła dopisana za blokiem CMS-a nie obsłużyła starego URL-a — otrzymaliśmy 404. Po poprawieniu kolejności wróciło 301.
Wynik: 12 kontroli integracji oraz 2 kontrole wycofania. Przywrócono dokładną zawartość wcześniejszego .htaccess i usunięto wyłącznie własną stronę testową; zbiór wcześniejszych identyfikatorów stron pozostał bez zmian. To dowód jednego scenariusza kolejności z działającym CMS-em, nie gwarancja zachowania każdej wtyczki przekierowań, konfiguracji multisite lub proxy.
Aby odtworzyć opcjonalny test, najpierw przygotuj i ukończ izolowane laboratorium source/restore z poradnika kopii i odtwarzania WordPress. Następnie użyj test-wordpress.mjs z tego ZIP-a, przekazując ścieżkę do folderu z compose.yaml. Szczegóły i warunki wyłącznego dostępu opisuje README. Nie przekazuj ścieżki swojego projektu produkcyjnego.
301 i 308 mogą być przechowywane w cache. Przywrócenie pliku na serwerze nie cofa natychmiast odpowiedzi zapisanej wcześniej w przeglądarce lub CDN. Dlatego po rollbacku porównaj świeży odczyt curl z przeglądarką i w razie potrzeby usuń właściwe wpisy cache zgodnie z instrukcją operatora. Nie twórz odwrotnego przekierowania B → A w ciemno, bo klient z zapamiętanym A → B może wejść w pętlę. Podstawa: RFC 9110: statusy i cache.
Nie oceniaj nowego adresu wyłącznie po HTTP 200. W pakiecie dodaliśmy check-destinations.py: odczytuje dokładnie wskazane strony, sprawdza canonical w head, meta robots/googlebot i nagłówki X-Robots-Tag, tytuł, H1 oraz zatwierdzone frazy treści. To własny profil odbioru statycznego HTML, nie implementacja algorytmu Google ani automatyczna ocena jakości. Dobierz wymagane fragmenty tak, aby odróżniały właściwą ofertę od homepage lub komunikatu błędu.
[
{
"path": "/oferta.html",
"canonical": "https://sklep.example/oferta.html",
"titleContains": "Integracja magazynu",
"h1Contains": "Integracja magazynu",
"requiredText": ["Synchronizacja stanów", "Obsługiwane systemy"]
}
]python3 check-destinations.py --origin 'https://sklep.example' --manifest cele-seo.jsonTen ścisły wariant wymaga jednego absolutnego canonicala w HTML head w tym samym originie, jednego H1 i jawnie wskazanej treści. Odrzuca noindex/none dla Googlebota oraz łańcuch. HTTP Link canonical, element base i unavailable_after kieruje do osobnego przeglądu zamiast zgadywać wynik. Czyta do 1 MiB HTML; większa odpowiedź nie otrzyma częściowego PASS. Nie renderuje JS, nie ocenia widoczności CSS i nie interpretuje robots.txt. Zgodność kilku fraz nie dowodzi równoważności całej treści — tę decyzję nadal zatwierdza właściciel mapy.
Kryteria odbioru opieramy na dokumentacji Google: canonical i meta robots oraz X-Robots-Tag. Zestaw 47 testów obejmuje również celowo zły canonical, blokadę indeksacji w nagłówku, duplikat H1, treść obecną tylko w skrypcie, limit rozmiaru odpowiedzi i brak podążania do obcej domeny.
Google zaleca utrzymywać przekierowania możliwie długo, zwykle co najmniej rok; dla użytkowników starych linków mogą być potrzebne bezterminowo. Jednocześnie warto aktualizować własne linki oraz ważne odsyłacze zewnętrzne, aby nie opierały się na dodatkowym skoku. Źródło: Google: wdrożenie i monitoring migracji URL.
Jeżeli po migracji adres końcowy działa, ale ma błędny canonical lub noindex, kolejny krok to diagnostyka HTML. Pokazujemy ją na przykładzie sklepu w poradniku technicznego SEO PrestaShop. Przy zmianie struktury całej witryny zakres mapy i odbioru możesz uzgodnić w ramach wdrożenia technicznego SEO.
Prześlij starą i planowaną strukturę URL oraz informację o hostingu i CMS-ie. Ustalimy zakres mapy przekierowań, testów oraz monitoringu po migracji. Sam kod 301 nie zastępuje sprawdzenia treści i indeksacji.
Sprawdź zakres technicznego SEOStrony firmowe, landing page i serwisy w WordPress oraz Next.js — projektowane pod konwersję i widoczność w Google.
Tworzenie stron WWWProjektowanie UX/UIStrony dla firmLanding pageRedesign strony
Sklepy na PrestaShop i WooCommerce — wdrożenia, migracje i dedykowane moduły pisane przez zespół senior developerów.
Tworzenie sklepówNowy sklep PrestaShopMigracja na PrestaShopModuły PrestaShopIntegracja z hurtowniami
Pozycjonowanie, SEO techniczne i optymalizacja konwersji — widoczność, kliknięcia i zarejestrowane konwersje porównywane z baseline'em.
SEO dla firmLokalne SEOContent SEOAudyt SEOSEO techniczne
Utrzymanie, aktualizacje i stały rozwój — bierzemy odpowiedzialność za obszar techniczny również po starcie projektu.
Opieka nad stronąAktualizacje i serwerSupport w pakietach godzinRozwój stronyWdrożenia niestandardowe
Bezpłatna konsultacja
Opisz, czego potrzebujesz — zaproponujemy zakres, technologię i realny harmonogram. Bez zobowiązań.
Następny krok
Krótka rozmowa albo wiadomość — wrócimy z konkretną propozycją zakresu.
Formularz kontaktowyOpisz, czego potrzebujesz — strony, sklepu, integracji czy wsparcia technicznego. Odpowiemy z konkretnymi propozycjami i orientacyjną wyceną, zwykle w ciągu 24–48 godzin roboczych.
ul. Henryka Pachońskiego 7a
31-223 Kraków
Biuro czynne: pon.–pt., 9:00–17:00
Webalpha
NIP: 6562340974
REGON: 385895790