unity wiedza

Czas czytania: 8 minut

Problem SEO w aplikacji SPA, czyli o Server Side Rendering

SSR, czyli kolejny element układanki aplikacji SPA. W poprzednim artykule przedstawiono, czym jest aplikacja typu SPA i jakie są zalety tego rozwiązania. Jeśli zależy nam na SEO, nieodzownym elementem będzie SSR – Server Side Rendering

Z poniższego artykułu dowiesz się:

  • Co to jest SSR i kiedy go potrzebujemy 
  • Jakie są sposoby na renderowanie i który z nich wybrać 
  • UseCase, czyli jak to działa naprawdę 
  • Jak dbać o wydajność serwera i bezpieczeństwo 

Czym jest SSR? 

Server Side Rendering, czyli renderowanie po stronie serwera, jest niczym innym jak serwowaniem wygenerowanego kodu HTML danej strony. Oznacza to, że odbiorca dostaje gotowy kod html, do zaprezentowania w przeglądarce. Bez SSR, odbiorca (użytkownik lub bot), otrzyma „pusty” HTML. Porównanie na obrazku poniżej. 

Rys. 1. Serwer Side Rendering (źródło własne).

Po lewej kod wyrenderowany po stronie serwera, po prawej kod wejściowy aplikacji SPA.  

Wszystkie aplikacje niebędące typu SPA, renderowane są właśnie po stronie serwera. Kod HTML składany jest nie w przeglądarce użytkownika, a na serwerze. W czasach aplikacji SPA nadal musimy tak działać – dlaczego? Wyjaśnienie znajdziesz poniżej.

Zysk z zastosowania SSR 

Rys. 2. Zysk z zastosowania Server Side Rendering.

Sposoby renderowania 

Możemy wyróżnić dwa podejścia do SSR: 

  • Natywnie (Next.js, Nuxt.js, …) 

Dzięki Node.js możemy uruchomić kod JavaScript po stronie serwera, wygenerować HTML i wysyłać go w odpowiedzi na żądanie.  

Dokładniej mówiąc, gdy przychodzi żądanie o stronę, kod JavaScript naszej aplikacji uruchamia się w Node.js. Realizuje to dostępny do danego frameworka/biblioteki dodatek, odpowiedzialny za renderowanie po stronie serwera, np. Next czy NuxtTaki kod działa trochę inaczej, gdyż nie mamy obiektu nadrzędnego window, jaki zapewnia przeglądarka. Mamy za to dostępne metody do pobrania wyrenderowanego HTML’a. 

  • Z użyciem silnika przeglądarki (HeadlessChromePhantomJs, Prerender.io, …)

Drugim sposobem jest użycie silnika przeglądarki, dzięki któremu strona naszej aplikacji uruchomi się dokładnie tak samo, jak u użytkownika. Z pomocą nadal przychodzi Node.js, za którego pośrednictwem uruchamiamy instancję np. HeadlessChrome’a. Tak, przeglądarka po stronie serwera J. Jest to, w dużym uproszczeniu mówiąc, przeglądarka Chrome bez graficznego interfejsu. Mając uruchomioną taką instancję, możemy przekazać adres url strony, którą ma uruchomić przeglądarka. Po zakończeniu wczytywania się strony, za pomocą dostępnego API, pobieramy wygenerowany HTML i przesyłamy w odpowiedzi na żądanie. 

Obydwa podejścia mają wady i zalety. Przyjrzyjmy się im bliżej. 

Architektura – przepływ żądań 

Co jest nam potrzebne, aby stworzyć własny SSR: 

  • serwer lub kontener, na którym uruchomimy poniższe usługi
  • Node.js 
  • serwer http, np. Express 
  • nasza aplikacja w JavaScript lub silnik przeglądarki np. HeadlessChrome
    (to jedyna różnica w obydwu podejściach)

Rys. 3. Architektura serwerów – podejście natywne i podejście używające przeglądarki.

Co właściwie się dzieje po stronie serwera SSR? Przyjmijmy pewną architekturę serwerów, przedstawioną na obrazku. Gdy używamy natywnego podejścia, nasz serwer SSR, działa jak standardowy serwer http, przyjmuje żądanie, przetwarza, zwraca odpowiedź.  

W podejściu z renderowaniem przeglądarkowym pojawia się jednak pewne utrudnienie. Url żądania, które otrzymujemy, wstawiamy do przeglądarki w przykładzie HeadlessChromePrzeglądarka wysyła request, w tym przypadku na ten sam adres co przed chwilą odebraliśmy. Serwery, które analizują żądania i kierują w odpowiednie miejsce muszą odróżnić, że request pochodzi nie od użytkownika, a z naszego serwera SSR i skierować go tym razem tam, gdzie leżą pliki naszej aplikacji SPA, a dokładniej interesuje nas główny plik HTML aplikacji. W przeciwnym razie request wpadłby w nieskończoną pętlę między serwerem SSR, a tym odbierającym żądania od użytkownika. 

UseCase: e-commerce, wersja z HedlessChrome 

Nasz przypadek. Aplikacja e-commerce. Wersja SSR z użyciem HeadlessChrome. Dwa serwery SSR, równolegle obsługujące cały ruch. 

Liczby, liczby 

Ruch użytkowników na poziomie (dane z UA): 

  • ~60000 sesji dziennie,  
  • ~3 odsłony/sesję 

Ruch na serwerach SSR: 

  • ~4 otwarte karty w HeadlessChrome w każdej sekundzie (czas renderowania jednej strony 2-4 sekund) 
  • ~110000 żądań dziennie 

Jak rozumieć te liczby? W standardowej aplikacji (nie SPA), oznaczałoby to, że:  

liczba sesji*liczba odsłon = liczba żądań do serwera 

W aplikacji typu SPA liczba żądań do serwera to liczba sesji. Dlatego, że tylko pierwsze wejście na stronę wykonuje żądanie po aplikację (to żądanie trafia do naszego serwera SSR). Dalsze poruszanie się po aplikacji pobiera tylko dane i nie angażuje już serwera SSR.  

Czujny czytelnik zauważy, że coś tu się nie zgadza. Skoro liczba sesji w UA = liczbie żądań do serwera SSR, to dlaczego UA mówi, że jest dużo mniej żądań niż obsługuje faktycznie SSR? Tak jest, ponieważ dane w UA są mniejsze o żądania robotów. A tych jest ok. 50% całego ruchu – aż tyle. To są dane z produkcyjnie działającej aplikacji. Każda aplikacji może mieć faktycznie inną ilość ruchu botów. 

Wydajność 

Zapewne wiele osób słysząc o przeglądarce po stronie serwera pomyślało: „Zaraz, a czy to w ogóle działa wydajnie?” Znając już liczbę żądań i czas obsługi każdego żądania zobaczmy, ile zasobów serwerowych potrzebujemy, aby to obsłużyć. W naszej architekturze serwerów mamy dwa serwery SSR, na które równomiernie rozkładamy nasz ruch.

Rys. 4. Jeden z dwóch dostępnych serwerów SSR – dzienne użycie CPU i RAM (źródło własne).

Czas renderowania strony przez serwer SSR jest zależny od naszej aplikacji frontowej i backendowej. Serwer SSR ma tu mały wpływ, gdyż narzut na uruchomienie karty w przeglądarce HeadlessChrome jest raczej niewielki, w stosunku do czasu wyrenderowania strony. HeadlessChrome ma za to wpływ na użycie CPU i RAM naszego serwera, co widać przy skokach ruchu w naszej aplikacji. 

Jak poprawić wydajność serwera SSR? 

  • Blokować niepotrzebne requesty, zewnętrzne skrypty. 

Po stronie serwera niepotrzebne nam są CSSy, obrazki, kody śledzenia i inne dodatki, które mogą tylko opóźnić renderowanie 

  • Nie renderować niepotrzebnych elementów jak np. popupy. 

Lepszy UX, gdy popup pokaże się użytkownikowi po chwili, a szybciej dostanie kontent. Dodatkowo wiele elementów, które nie są potrzebne do SEO, nie muszą być renderowane po stronie serwera. Przyspieszy to działanie aplikacji, a użytkownik dostanie szybciej główny kontent strony. 

  • Cachować zasoby. 

Bez SSR, wielu użytkowników wchodzących na ten sam adres url, musiałoby pobrać te same dane. Dzięki SSR i możliwości cachowania zasobów, dane dotyczące tej samej strony, pobiorą się tylko raz dla wszystkich użytkowników. 

HeadlessChrome robi to jak przeglądarka, czyli załatwia sprawę za nas. To samo można zrealizować w JavaScript, w aplikacji uruchomionej po stronie serwera, dzięki czemu bez względu na sposób renderowania, możemy cachować zasoby.

Bezpieczeństwo SSR

Gdy nasz kod JavaScript wykonuje się po stronie serwera, ataki typu XSS stają się jeszcze groźniejsze. Aby zminimalizować ryzyka związane z wykonaniem niechcianego kodu, należy zwrócić szczególną uwagę, aby: 

  • Blokować requesty spoza white listy. 

Wszystkie zewnętrzne skrypty i requesty powinny iść pod lupę, należy ograniczyć ich pobieranie do minimum.  

  • Nie pozwolić, aby dane użytkownika wyciekły poza sesję.  

W przypadku HeadlessChrome należy uważać na dane zapisywane w cookie i storage. Elementy przypisane stricte do użytkownika (ostatnio oglądane, ulubione, sesja koszyka, dane logowania, itp.) nie powinny wpływać na wygenerowany HTML innego użytkownika. Takie elementy najlepiej zablokować lub uruchamiać przeglądarkę w nowym kontekście dla każdego zapytania.
W wersji natywnego renderowania warto zwrócić uwagę na zmienne globalne i store aplikacji. Gotowe frameworki radzą sobie z tym problemem całkiem dobrze, jeśli jednak takowego nie używasz, istnieje ryzyko, że właśnie te miejsca okażą się luką w przepływie danych kontekstowych użytkowników. 

  • Zabezpieczyć dostęp do systemu plików. 

Jeśli nasza aplikacja nie potrzebuje dostępu do plików, warto zablokować dostęp do takiego API. Przeglądarka Chrome oferuje coraz to bogatsze API (system plików, płatności, obsługa peryferii, itp.), które może być dla nas niebezpieczne w przypadku ataku.

Porównanie obydwu podejść – zalety 

HeadlessChrome 

  • gotowe parametry do zabezpieczenia przepływu danych między użytkownikami, systemu plików 
  • obsługa asynchroniczności (przeglądarka mówi, kiedy strona skończyła renderowanie) 
  • możliwość renderowania HTML aplikacji niedostosowanej do SSR 
    • renderowaniu natywnym, należy pisać kod od początku dostosowany do SSR

Rys. 4. Łatwe parametryzowanie HeadlessChrome (źródło własne).

Renderowanie natywne 

  • mniejszy narzut na konfigurację serwerów (przepływ żądań) 
  • mniejsze wymagania sprzętowe 
  • łatwiejsze przekazywanie parametrów żądania do aplikacji  
  • w podejściu przeglądarkowym mamy tylko adres url z parametrami, natomiast w natywnym pełen dostęp do metod naszej aplikacji 

 Warto pamiętać: 

  • w podejściu natywnym po stronie serwera, nie ma obiektu np.: window – nie jesteśmy w przeglądarce 
  • należy używać bibliotek, które obsługują SSR 
  • niektóre pluginy należy wyłączyć, gdyż nie działają po stronie serwera lub są po prostu niepotrzebne (np.: loaderypopupy, obsługa gestów, itp.) 
  • jeśli chodzi o SEO i treści pokazywane na akcję użytkownika jak popupy, dla botów muszę być prezentowane w kodzie HTML 
  • w podejściu przeglądarkowym, w aplikacji RWD, należy dostosować style tak, aby nie było wyliczanych wielkości wpisanych inlinowo (np.: swiper), gdyż to spowoduje, że użytkownik zobaczy treść dostosowaną do innej szerokości ekranu, niż posiada faktycznie (w HeadlessChrome podajemy rozmiar ekranu „ViewPort”). Dopiero po uruchomieniu kodu JavaScript, style zostaną przeliczone na nowo. Warto unikać takich „skoków” wyglądu.

Podsumowanie

Kiedy potrzebujemy SSR? 

Przy aplikacji SPA, gdy zależy nam na SEO lub szybkości ładowania pierwszej strony użytkownikowi. 

Który sposób renderowania powinniśmy wybrać? 

  • Renderowanie natywne 

Gdy piszemy nową aplikację. Korzystajmy z rozwiązań oferowanych przez framework/bibliotekę – renderujmy natywnie przez JavaScript. Natywny sposób jest tym „poprawnym” pod względem wydajności i spójności z aplikacją, co narzuca nam również sposób pisania samej aplikacji. Możemy bez problemu wywoływać metody, akcje, bezpośrednio z naszej aplikacji. 

  • Renderowanie przeglądarką 

Gdy napisaliśmy aplikację bez SEO lub po prostu mamy istniejącą aplikację, modernizujemy ją. HeadlessChrome jest uniwersalnym rozwiązaniem, pozwala zrobić SSR bez wielu zmian w aplikacji. Nie jest czuły na globalne zmienne, łatwo wyłączymy dostęp do cache`u i innych newralgicznych miejsc.   

Należy obserwować rozwój frameworków. To, co dziś jest zaletą HeadlessChrome, być może w niedalekiej przyszłości będzie standardem w podejściu natywnym. 

Jeżeli chcesz dowiedzieć się więcej o usługach związanych z architekturą aplikacyjną, zapraszamy do zapoznania się z naszą ofertą.

 

unity

unity

Skontaktuj się z profesjonalnym doradcą IT

Napisz do nas

Wyrażam zgodę na przetwarzanie danych osobowych na zasadach określonych w polityce prywatności. Jeśli nie wyrażasz zgody na wykorzystywanie cookies we wskazanych w niej celach, w tym do profilowania, prosimy o wyłącznie cookies w przeglądarce lub opuszczenie serwisu. więcej

Akceptuj