Strona w Angularze
Oczywiście na powyższe pytania jest więcej niż jedno rozwiązanie, postaram się dalej o nich napisać. Ale przede wszystkim warto zapytać o coś innego: Dlaczego w ogóle chciałbym indeksować aplikację napisaną w Angularze? Jaką treść zazwyczaj chce się promować w Internecie? Tekst, zdjęcia, filmy, czyli generalnie treść statyczną, strony internetowe. Do czego zazwyczaj wykorzystuje się AngularJS? Do tworzenia Single Page Applications (SPA). Co to ma wspólnego? Raczej niewiele. Po co w ogóle indeksować aplikacje?
Bardzo często poszukiwanie rozwiązania problemu SEO w Angularze wynika ze złego doboru technologii do zadania. Zdarza się, że konstruowana jest rozbudowana aplikacja internetowa, do której AngularJS nadaj się świetnie, a do tego tworzony jest landing page, który niejako przy okazji oparty jest również o Angulara. Landing page to zazwyczaj wymienione wcześniej tekst, zdjęcia, filmy – czyli właśnie treść statyczna, czasem z elementami interaktywnymi. Dokładnie coś, co łatwo i bez wysiłku się indeksuje. Czy w ogóle framework jest potrzebny do stworzenia takiej strony? Zdecydowanie nie. Czy czasem jego użycie jest uzasadnione? Tak, ale rzadko. Moim zdaniem w większości przypadków jest to dobór niewłaściwego narzędzia do zagadnienia, przez co pojawia się tylko więcej trudniejszych problemów do rozwiązania – na przykład właśnie SEO.
Krótko: Aby nie mieć problemów z SEO w Angularze, nie twórz stron www w Angularze, bo na stronach internetowych nie potrzeba Angulara. Jeśli tworzysz aplikację i jej landing page to niech aplikacja korzysta z frameworka, ale landing page będzie już zwykłym HTML-em. To jest aż tak proste.
Dlaczego AngularJS się nie indeksuje?
Załóżmy jednak, że aplikacja już istnieje i po prostu konieczne jest zaindeksowanie treści, które się w niej znajdują, niezależnie od tego, czy jest to ten 1% przypadków, kiedy to rzeczywiście uzasadnione, czy wcześniejszy błąd projektowy. Jak już wspomniałem, rozwiązań jest kilka, ale aby je dobrze zrozumieć należy poznać naturę problemów SEO w Angularze.
Przede wszystkim warto zwrócić uwagę, że dzisiaj rzadko chodzi wyłącznie o indeksowanie się treści w Google. Bardzo istotnym nośnikiem informacji stały się przecież media społecznościowe, więc należy celować również w Facebooka i Twittera, a w zależności od potrzeb może też Slacka (chodzi o podgląd treści po wklejeniu linka). Na czym polega problem? Gdy udostępnia się link np. na Facebooku to bot Facebooka pobiera treść linkowanej strony, przetwarza i generuje na tej podstawie jej podgląd. Co istotne, pobiera wyłącznie HTML serwowany pod tym adresem i nie dotyka w ogóle kodu JavaScript, który się tam może znaleźć – czyli nie uruchamia np. aplikacji napisanej w Angularze. Problem polega na tym, że w przypadku Single Page Applications serwowany początkowo jest zawsze ten sam plik HTML niezależnie od podstrony, do której linkujemy. Po otwarciu takiego pliku w przeglądarce uruchamia się kod JavaScript, który dynamicznie zmienia zawartość strony. Niestety ten krok nie ma miejsca w przypadku pobierania treści przez boty i stąd one zawsze widzą tylko jedną i tę samą stronę, nieważne jaki byłby adres URL.
Wyjątkiem od tej reguły jest bot Google, który od pewnego już czasu potrafi uruchamiać JavaScript na stronach i dzięki temu poprawnie indeksuje także SPA. Jednakże łatwo znaleźć w Internecie komentarze osób, które donoszą, że sam fakt uruchamiania JavaScriptu przez bota nie oznacza automatycznie poprawnego indeksowania aplikacji, a ponadto jeśli zależy nam na wspomnianych serwisach społecznościowych to i tak zmuszeni jesteśmy zastosować jedną z metod opisanych poniżej.
SEO w AngularJS
Jest kilka sposobów na rozwiązanie opisanego problemu, ale zasadniczo wszystkie sprowadzają się do jednego: Serwowania statycznego HTML-a botom. Zasada działania jest prosta. Najpierw pewien przygotowany skrypt pobiera poszczególne podstrony, uruchamia na nich JavaScript i zapisuje dynamicznie wygenerowany HTML. Następnie na poziomie serwowania treści rozpoznaje się czy żądanie jest wykonane przez bota, czy nie. Jeśli tak to na podstawie adresu URL serwowany jest wcześniej przygotowany statyczny HTML.
prerender.io
Prerender.io jest serwisem typu SaaS oraz biblioteką Open Source. Zasada działania jest dokładnie taka, jak opisana wcześniej: Prerender pobiera wskazane podstrony, wykonuje na nich JS, cache’uje statyczny HTML i następnie serwuje botom. Wykorzystanie Prerender.io wymaga zmian w konfiguracji serwera, z którego się korzysta – ale na szczęście twórcy oraz społeczność udostępniają odpowiednie middleware m.in do Nginx, ExpressJS, Rails, Apache, Spring, Symfony 2 i wielu innych. Szczegóły konfiguracji opisane są w dokumentacji.
Prerender SaaS jest bezpłatny do 250 podstron co bez wątpienia jest jego ogromną zaletą. Dodatkowo, w przypadku gdy bot spróbuje pobrać stronę, która jeszcze nie znajduje się w cache’u Prerendera, zostanie ona w locie pobrana i dodana do cache. Może to zająć chwilę dłużej, co z kolei może skutkować obniżeniem rankingu przez bota, jednak z drugiej strony to na pewno bardzo elastyczne rozwiązanie. Dodatkowo Prerender pozwala też na ręczne wymuszenie dodania nowych podstron do cache’a przy użyciu odpowiedniego zapytania POST
do API.
BromBone
BromBone jest drugim serwisem co do zasady działania bardzo podobnym do Prerender.io. Różnica polega na tym, że domyślnie BromBone wczytuje strony do indeksowania z pliku sitemap.xml stworzonego na podstawie specyfikacji Sitemap.xml. BromBone monitoruje zmiany w sitemapie i automatycznie cache’uje nowe podstrony co około 6 godzin. Bardziej elastyczne możliwości dostępne są tylko dla użytkowników planów Enterprise. BromBone nie udostępnia też planu darmowego, a opcja do 200 podstron kosztuje $39 miesięcznie.
PhantomJS
Trochę inną możliwością jest skorzystanie z własnego skryptu napisanego w JavaScript, przygotowanego do użycia razem z PhantomJS. PhantomJS jest przeglądarką internetową bez graficznego interfejsu użytkownika (headless browser). Jest oparta o WebKit i posiada API w JavaScripcie. PhantomJS potrafi renderować strony www oraz aplikacje napisane w JavaScripcie bez konieczności uruchamiania środowiska graficznego, a więc na przykład może to robić na serwerze, w tle. Brzmi idealnie jako narzędzie do renderowania statycznego HTML-a dla botów.
Wadą tego rozwiązania bez wątpienia jest nieco bardziej skomplikowana konfiguracja – trzeba napisać skrypt, który poprawnie obsłuży naszą aplikację. Jednocześnie jest to również zaleta, ponieważ tę konfigurację można maksymalnie dostosować do naszych potrzeb; to na pewno najbardziej elastyczne rozwiązanie. Przykładowy skrypt można znaleźć w repozytorium angular-seo.
Content cloaking
Wielu użytkowników wyraża obawy w związku z serwowaniem innej treści normalnym użytkownikom, a innej botom w związku z faktem, że może to być postrzegane jako przykład maskowania zdefiniowanego przez Google:
Maskowanie to działanie, które polega na przedstawieniu wyszukiwarkom innych treści lub adresów URL niż użytkownikom.
Dawniej technika ta służyła podbiciu rankingu danej strony w wynikach wyszukiwania (np. poprzez serwowanie botom Google treści wypchanej słowami kluczowymi, a użytkownikom zwykłego artykułu). Z tego powodu dzisiaj wyszukiwarki zdają się penalizować takie techniki. Jednak opisane przeze mnie metody nie są negatywnie punktowane przez boty z kilku powodów. Po pierwsze, serwowana treść w tym przypadku jest taka sama zarówno dla bota, jak i dla użytkownika – zmienia się tylko sposób jej podania (dynamicznie stworzony HTML vs statyczny HTML). Ponadto, Google samo dawniej stworzyło wskazówki mające na celu umożliwienie indeksowania Single Page Applications. Co prawda dzisiaj są one już niezalecane (w związku z dodaniem obsługi JavaScriptu do bota Google), ale sam fakt istnienia tej dokumentacji wskazuje na to, że serwowanie statycznego HTML botowi Google na pewno nie było nigdy karane.
Podsumowanie
Korzystałem zarówno z BromBone, jak i Prerender.io, i oba te rozwiązania dobrze się sprawdziły – robiły dokładnie to, co powinny. Jednak ostatecznie zrezygnowaliśmy z nich na rzecz własnego skryptu i PhantomJS. Ostatnio implementowałem możliwość udostępniania na Facebooku dynamicznie generowanych linków do podstrony w aplikacji i tu już niestety gotowe rozwiązania się nie sprawdziły. BromBone nie serwował zupełnie niczego, bo potrzebuje aż 6 godzin na zaindeksowanie nowych linków. W prerender.io natomiast szybko przekroczyliśmy limit 250, a nawet kilku tysięcy podstron, co po prostu przestało się opłacać. Własny skrypt i PhantomJS wymagały poświęcenia trochę więcej czasu na skonfigurowanie, jednak ostatecznie okazały się mniej zawodne i tańsze.
Bez wątpienia z tego wpisu należy wyciągnąć dwa wnioski:
- Do stworzenia strony www nie potrzeba frameworka.
- Jeśli zajdzie potrzeba, to nie trzeba już dzisiaj obawiać się SEO w aplikacjach JavaScriptowych.
Wkrótce podzielę się na GitHubie swoją konfiguracją i sposobem tworzenia odpowiednich meta-tagów dla podstron w AngularJS.