skip to Main Content
+48 606 628 628 info@zgred.pl
Http2 Foto

Dopalanie strony za pomocą HTTP/2

1. HTTP/2 – co nowego

Mija już ponad rok od ukończenia prac nad nową wersją protokołu HTTP/1.x o nazwie HTTP/2. Nowa wersja bardzo mocno bazuje na googlowskim protokole SPDY ale  w przeciwieństwie do niego nie wymaga TSL (SSL) ale jest to właściwość teoretyczna o czym wspomnę w dalszej części artykułu. Nowa wersja jest zgodna wstecznie co oznacza, że do poprawnego funkcjonowania nie są konieczne żadne zmiany w kodzie Twojej witryny.

W sieci jest sporo dyskusji , w których trwa spór o to czy HTTP/2 spełnia oczekiwania czy nie. Jedni dowodzą, że tak, inni odwrotne. Na pewno jest to krok do przodu. Inną kwestią czy jest to krok siedmiomilowy czy dużo mniejszy. W poniższym artykule nie będę jednak zagłębiał się w technikalia i optował za którąś ze stron.

Przyjrzyjmy się co wnosi (na bardzo ogólnym poziomie) HTTP/2:

  • Protokół jest binarny w przeciwieństwie do swojego poprzednika, który był tekstowy. Dzięki temu jest bardziej “strawny” dla oprogramowania serwerowego i klienckiego. Łatwiej i szybciej się go parsuje (odpada translacja tekst <-> kod binarny) a przede wszystkim jest mniejsze ryzyko powstania błędu. Binarność ma też swoją wadę – praktycznie uniemożliwia obsługę tego protokołu przez “interfejs białkowy” za pomocą np. sesji telnetowej (ale są / powstaną odpowiednie nakładki / wtyczki).
  • Wprowadza kompresję nagłówków http. Teoretycznie powinno to wpłynąć w jakiś sposób na poprawę szybkości ładowania strony ale w praktyce bywa różnie. Zależy to od ilości danych przepływającej w nagłówkach pomiędzy serwerem a klientem (przeglądarką). W komunikacji np.  z serwisem Facebook może to być w jakimiś stopniu odczuwalne (duży udział procentowy informacji w nagłówkach http w stosunku do danych). Kosztem kompresji / dekompresji jest obciążenie procesorów serwera i przeglądarki.
  • Jedno połączenie TCP wiele żądań http (multiplexing) i priorytet zasobu. W starej wersji można było wysłać tylko jedno żądanie o zasób w ramach sesji TCP. Przeglądarka wysyłała żądanie o zasób, otrzymywała dane i dopiero mogła wysłać następne. Obejściem tego problemu po stronie przeglądarki było otwieranie wielu sesji TCP (ilość ich zawsze jest limitowana). Do protokołu po stronie serwera wprowadzono możliwość otrzymywania wielu żądań bez otrzymywania odpowiedzi na poprzednie ale tu nie przeskoczono problemu po stronie serwera – konieczność przetwarzania żądań wg. kolejności ich otrzymywania (np. pierwszy duży plik blokował kolejne małe ale istotniejsze do renderowania strony). Dla zainteresowanych – dochodzi jeszcze problematyczna właściwość połączenia TCP tzw. slow start.

http

W HTTP/2 wprowadzono możliwość wysyłania wielu żądań http w ramach jednej sesji połączenia TCP. Oczywiście połączenie TCP jest per host. Jeżeli ktoś korzysta z zasobów z własnego hostingu oraz np. z zasobów jakiegoś CDNa to zostaną utworzone 2 połączenia. Każde żądanie jest strumieniem (w połączeniu TCP),  współdzielonym przez klienta i serwer, które mogą być zamykane lub odrzucane. Przeglądarka wysyła kilka żądań i ustawia ich priorytet, który informuje serwer o kolejności zwracania zasobów (np. najpierw zasób html, css, js,  obrazek). Serwer stara się (jeżeli się da) obsługiwać w pierwszej kolejności żądania z wyższą wagą.

Dzięki powyższym mechanizmom uzyskujemy redukcję ilości połączeń a tym samym zmniejszenie zasobów serwera i klienta koniecznych do utrzymywania wielu sesji TCP, redukcję ilości przesyłanych pakietów, zredukowanie szansy na stratę pakietów, szybsze renderowanie strony przez przeglądarkę.

http2

  • Push z serwera. Dla programistów temat “rzeka”. Jednym zdaniem: umożliwia serwerowi wysyłanie danych do klienta bez wcześniejszego żądania. W niniejszym artykule skupię się właśnie na wykorzystaniu tej technologi w celu poprawy szybkości wczytywania strony. Resztę profitów wynikających z użycia głównych zmian w HTTP2 dostajemy niejako “za darmo”. Proszę się jednak nie przerażać, nie będę proponował grzebania w kodzie Waszych witryn, nie będzie wymagana praktycznie żadna tajemna wiedza programistyczna. Po prostu pokażę jak poustawiać kilka “wajch” w pliku htaccess :).

http2-push

 

2. Wdrożenie HTTP2

Aktualnie praktycznie wszystkie liczące się na rynku przeglądarki mają zaimplementowaną obsługę nowego protokołu. Wg danych podawanych przez serwis caniuse.com aktualnie 76% internautów (w Polsce 83%), korzysta z takich przeglądarek. Generalnie strona kliencka jest przygotowana do serwowania jej treści za pomocą http2. Jest tylko małe “ale” – praktycznie wszystkie przeglądarki wymagają aby strona miała certyfikat ssl. Niby http2 nie wymaga posługiwania się SSL’em ale w rzeczywistości bez niego nie ma sensu optymalizacji witryny pod nowy protokół. Jest to kolejny powód do zastanowienia się nad zakupem certyfikatu dla naszych stron.

http2-browser

Aby nasza strona korzystała z dobrodziejstw nowego standardu oprócz obsługi po stronie klienta wymagana jest jeszcze jego obsługa po stronie serwera na którym jest hostowana. I tu niestety sprawa wygląda dużo gorzej. Ofert hostingu z obsługą HTTP2 jest jak na lekarstwo, w Polsce znalazłem chyba tylko dwie (na nowych wersjach serwera Litespeed). Z pozoru problem nie do przeskoczenia ale można go bardzo prosto rozwiązać za pomocą usługi cloudflare.com. Czym jest ta usługa i jakie korzyści (oprócz obsługi HTTP2 po stronie serwera) daje nie będę opisywał – temat na kilka artykułów. Wdrożenie usługi jest banalnie proste i sprowadza się do rekonfiguracji rekordów DNS naszej domeny – po dokładny opis jak to zrobić odsyłam do dokumentacji pomocy CloudFlare. Najważniejsza sprawa – dla naszych zastosowań w zupełności wystarczy darmowa wersja usługi :)

3. Dopalamy naszą witrynę.

Wszyscy klienci CloudFlare korzystający z HTTP2 maja domyślnie włączoną technologię Server Push ale tak jak wcześniej wspomniałem, to nie wystarczy. Należy jeszcze coś grzebnąć w naszym pliku htaccess aby cieszyć się z profitów tej technologi. Grzebanie polega na dodaniu dyrektywy nakazującej dodanie dodatkowego nagłówka http:

 

Header add Link '</sciezka/do/styl.css>; rel=preload; as=stylesheet'

Gdzie:

preload – informacja (dla serwera i klienta) że dany zasób będzie wysłany za pomocą push’a

as – typ zasobu, możliwe wartości przedstawiono w poniższej tabeli

http2article-table

Ścieżki do zasobów muszą być relatywne i “lokalne” nie mogą to być wskazania na obiekty spoza naszej domeny.

Nic nie zmieniamy w kodzie naszej witryny, nie usuwamy referencji do obiektów, które zadeklarowaliśmy jako link w htaccess! I to już właściwie wszystko :). O czym należy pamiętać? W dodawanych nagłówkach typu link należy wskazywać “uniwersalne” zasoby, które są ładowane na wszystkich stronach naszej witryny a więc globalne style, skrypty, obrazki logo, ikonki menu, fonty itp. Nie ma sensu wskazywać obrazka, który występuje tylko na jednej konkretnej stronie naszej witryny.

Jeżeli chcemy zoptymalizować szybkość konkretnej strony to proponuję użyć poniższej składni

 

<Files /sciezka/do/konkretna-strona.php>
   Header add Link '</sciezka/do/obrazek1.jpg>; rel=preload; as=image'

   Header add Link '</sciezka/do/obrazek2.jpg>; rel=preload; as=image'

…..

</Files>

 

Jeżeli optymalizowana ma być grupa stron, którą da się zidentyfikować za pomocą wyrażenia regularnego to proponuję zastosować FilesMatch.

4. Testy HTTP2

Warunki testu referencyjnego:

  • strona hostowana na środowisku z zainstalowaną usługą cloudFlare – w ten sposób zniwelowano wpływ jaki daje sama usługa
  • na stronie nie ma referencji do obiektów zewnętrznych (nie hostowanych poza domeną), wyjątkiem są skrypty GTM i GA

Warunki testu optymalizacji:

  • warunki dla strony takie same jak w teście referencyjnym
  • zidentyfikowano zasoby wspólne (12) dla całej witryny i dla nich stopniowo wprowadzono odpowiednie zapisy w pliku htaccess.

 

Wynik testu:

1
2
3

Jak widać z powyższych wykresów czas ładowania strony spadł z ponad 3 [s] do 1.4 [s].

Jak należało się spodziewać ilość żądań zmalała. Na wykresie widać spadek wagi strony ale wynika to z tego że GTmetrix nie sumuje danych otrzymanych za pomocą pusha. Tak naprawdę waga strony się nie zmieniła. Generalnie można się spodziewać skrócenia czasu ładowania strony średnio o 30% – 40%. Myślę, że gra warta świeczki :)

Podczas testów zdarzało się, że wyniki były niestabilne, tak jakby GTMetrix nie zawsze dawał sobie radę z pushem lub mogło to być wynikiem jakiś jego wewnętrzych keszów, brakiem odświeżenia CDNa CloudFlara (zapewne korzysta z lokalizacji CDNa w Kanadzie) itp. Poszczególne wyniki optymalizacji bardzo ładnie widać w narzędziach deweloperskich jakie są dostępne w Chormie i Firefoxie (wykresy czasowe).

Po optymalizacji okazało się, że stosunkowo dużym obciążeniem czasowym jaki pozostał są skrypty googla (GTM i GA). Teoretycznie nie da się ich optymalizować ale widzę furtkę aby coś tam pokombinować. Inną możliwością jest poczekanie na zakończenie prac przez W3C i obsługę przez przeglądarki atrybutu HTML: preload.

W czasie testów wyszła jedna sprawa. Do tej pory zalecane było umieszczanie skryptów js na końcu dokumentu HTML. Przy wykorzystaniu server push korzystniejszym jest umieszczanie takich zasobów w sekcji head. Zauważyłem, że jak skrypt był umieszczony za końcem sekcji body bardzo często zdarzało się, że występowało żądanie tego zasobu (pomimo wcześniejszego dostarczenia go za pomocą push). Niestety takie umieszczenie skryptów może spowodować obniżenie oceny za szybkość działania strony (komunikat o kodzie blokującym renderowanie) w narzędziu googla do testowania szybkości strony. Prawdopodobnie GoogleSpeed Insights nie jest dostosowane do HTTP2 i zgłasza coś co nie występuje.

5. Co na to Google – co oni mówią o HTTP2 ?

Pod koniec roku 2015 John Mueller zapowiedział, że najpóźniej do początku roku 2016 googlebot będzie obsługiwał HTTP2

Czy tak się stało – nie wiem. W GSC w opcji pobierz jako Googlebot nie ma problemów dla stron z HTTP2 jednak pobieranie odbywa się po starej wersji protokołu. W opcji statystyki indeksowania, linia czasu spędzonego na pobieraniu strony jakby się “wygładziła” – mniejsze piki (średni czas pobierania spada).

 

 

Obrazek tytułowy pochodzi z serwisu: media.licdn.com

1 gwiazdka2 gwiazdki3 gwiazdki4 gwiazdki5 gwiazdek (11 głosów, średnia: 4,64 z 5)

Ten wpis ma 40 komentarzy
  1. Chyba rozważę zabawę w http2 na swojej stronie. Może jeszcze nie teraz, ale w każdym bądź razie w najbliższej przyszłości na pewno. Tymczasem artykuł leci do zakładek :)

  2. Nie wiem do czego się odnosi te 7.7x (szybkość ładowania strony, nawiązanie połączenia itp)?
    Testy jakie przeprowadziłem na potrzeby tego artykułu dały wynik ok 2x szybsze ładowanie strony. Dla mnie jest to wystarczający argument.

    1. Ano warto… o czym nie omieszkałem wspomnieć w 2 miejscach tego artykułu + taką informację można wywnioskować z obrazka z wersjami przeglądarek wspierającymi HTTP2 :)

  3. Ku ścisłości – w dwóch miejscach podajecie różne sposoby wpisu do .htaccess:

    Header add Link ‚/sciezka/do/styl.css>; rel=preload; as=stylesheet’
    Header add Link ‚; rel=preload; as=image’

    raz jest ‚ścieżka’>, drugim razem – delikatnie wprowadzając w błąd :)

    1. cóż, nie można trójkątnych dawać w komentarzu, także chodzi o to, że raz otwieracie nawias trójkątny, a raz nie :)

    2. Ok. Dzieki. Poprawione. Było ok tylko podczas walki z automatycznym poprawianiem przez wp na „polski apostrof” zjadło ten znaczek. Sam zresztą widzisz co się stało z Twoim tekstem komentarza.

  4. Czy po wprowadzeniu HTTP/2 nie zaczęły Ci się pojawiać w Chrome błędy „Kurza twarz”? Testuję sobie kilka stron i przy przechodzeniu na podstrony lub nawet podczas wejścia na główną pojawia mi się losowo w/w błąd.

    1. Jak wprowadzałem to rozwiązanie w czerwcu to nie występowały takie problemy. Tydzień temu, chciałem poekserymentować jak będzie się zachowywala strona jeżeli podam push link do zewnętrznego zasoby (skrypt GA i GTM) i wtedy wystąpiło kilkukrotnie losowo „kurza twarz”. Problem może być po stronie Chroma, może w aktualizacji coś nawalili. Raczej wykluczam CloudFlare bo na FF to nie występowało ale nigdy nie wiadomo bo zapowiadali że będą kombinowali coś z „keszowaniem” danych wysyłanych przez push (w artykule pominąłem te zagadnienie). Generalnie zalecam aby przy testowaniu zmian włączyć w CloudFlare tryb developerski (masz gwarancję że nie dostaniesz danych z kesza CF).

      1. acha, tylko tryb developerski dość mocno (niekorzystnie) wpływa na czasy ładowania strony.
        Włączamy tryb developerski, wprowadzamy zmiany, testujemy czy jest ok, wyłączamy, testujemy wydajność czasową.

    2. jeszcze 2 uwagi:
      1. Chrome ma buga czasami (losowo) w konsoli pojawia się ostrzeżenie Warning: link rel=preload must have a valid as value`, pomimo tego że wszystko jest ok.
      2. GTmetrix ma jakieś własne „narzuty czasowe”. Wychodzi czas ładowania danej strony 1.5 [s] a w przeglądarce ta strona ładuje się w 0.5 – 0.7 [s]. Jednak na etapie testów nie ma to większego znaczenia – wyniki poszczególnych optymalizacji należy interpretować względem poprzednich.

      1. Widziałem te błędy w konsoli. Zauważyłem też, że na FF nie występują żadne błędy, więc chciałem się upewnić, czy to jakieś moje błędy w implementacji czy wina przeglądarki. Dziękuję za odpowiedź.

        1. Widzę że udało Ci się zejść z czasem ładowania strony 4[s] na 1.5[s]. Popróbuj jeszcze z zasobami hostowanymi na maxcdn’ach, przenieś je na swój hosting a tym samym przeniesiesz je na cdny CloudFlare. Nie przyspieszy to czasu pobierania ale być może spowoduje szybsze renderowanie strony (przeglądarka dostanie szybciej cssy, skrypty itp).

          1. Byłem zdziwiony, że czas ładowania zmniejszył się aż tak kolosalnie:)
            Jestem w trakcie pisania nowego motywu dla kursbootstrap .pl, tam skupię się na jak najlepszej implementacji HTTP/2. Skorzystam z rady aby nie pobierać plików z maxcdn i zobaczę co tam w testach wyjdzie.

            1. to prawda w niektórych przypadkach kopara opada :). Przykład podanych w artykule wyników optymalizacji to średniak (były gorsze ale lepsze też)

  5. Cloudflare na ten moment:
    Połączenie cloud serwer nasz serwer odbywa się po HTTP/1.x ;) a http2 tylko użytkownik Cloudflare… .

    https://support.cloudflare.com/hc/en-us/articles/200167976-How-can-I-tell-if-HTTP-2-or-SPDY-is-working-for-my-website-

    https://support.cloudflare.com/hc/en-us/articles/214534978-Are-the-HTTP-2-or-SPDY-protocols-supported-between-CloudFlare-and-the-origin-server-

    Choć warto pewnie być przygotowanym na także na http2 z cloud serwer nasz serwer, lub w razie rezygnacji z cloudflare.

    htaccess a może ngnix + php-fpm ;) ?

    1. >>Połączenie cloud serwer nasz serwer odbywa się po HTTP/1.x ;) a http2…
      tak, tylko nie ma to praktycznie żadnego wpływu. Samo http2 daje stosunkowo niewielkiego kopa. Dopiero wykorzystanie tricku z pushem daje wymierne efekty a push jest po stronie CF.
      >>Choć warto pewnie być przygotowanym na także na http2 z cloud serwer nasz serwer, lub w razie rezygnacji z cloudflare.
      Na nic nie trzeba być przygotowanym, jeżeli Twój serwer i CF będzie chodził po http2 to połączenie będzie w tym standardzie nic nie trzeba robić po Twojej stronie. Jeżeli rezygnujesz z CF to też nic nie robisz. Zostawiasz wpisy w htacess wykorzystywane przez push. Przeglądarka dostaje te nagłówki ale nie dostaje zasobów z pusha i te nagłówki olewa a więc jest po staremu (tak jak http1.x) i wszystko jest ok tylko nie masz przyszpieszenia renderowania strony.

    2. a jeżeli chodzi o niezależność od CF z zachowaniem tricku z pushem to bym się wstrzymał. CF dodawał swoje zmiany do publicznego repozytorium nginx’a. Z tą funkcjonalnością pewnie będzie podobnie (gdzieś widziałem dyskusję na ten temat ale na szybko nie mogę znaleźć).
      Generalnie ten prosty trick i efekty jakie daje stawia pod znakiem zapytania rozpatrywania wdrożenia googlowskiego apm (po co się z tym męczyć)

      1. APM jest wtyczka do WordPress generuję czyste wersje „mobilnych” artykułów od ręki i koniec ;) Ale w erze budżetowych czterordzeniowych mediateków z 1GB RAM +- 50$ taka prostota ma sens ?

        1. ano jest.. do server push też jest. Zamulić WP wtyczkami z dziurami to żaden problem. Wiem, wiem świat www na WP stoi, nawet jednostronowe wizytówki.

        2. Opisany trik jest uniwersalny niezależnie czy hostujesz stronę na WP, joomli, system sprzedażowy X lub Y. Wtyczki do WP nadają się tylko do WP. Proponuje nie zawężać tematu.

    3. DNS z cloudflare
      Gmetrix /
      VPS 3.19s
      VPS + CF free (Minify, Rocket, H2, Cache ) 1.28s

      Empirycznie patrząc na czystą kartę, adres strony enter i licząc sekundy
      VPS ~ 3.5 – 4s
      VPS + CF free (Minify, Rocket, H2, Cache) ~3s

      Nie ma przepaści ;) Może z Apache zamiast ngnix byłyby jakieś większe różnice.

      1. Nie znam warunków w jakich przeprowadziłeś test ale patrząc na zamieszczone wyniki widać że jest coś nie tak z testem. GTmetrix daj wyrażny sygnał że jest dużo lepiej (3.2 [s] do 1.3[s]). Jeśli wierzyć pomiarom przeglądarkowym to coś jest nie tak (swoją drogą polecam F12 i kliknięcie zakładki network – dużo bardziej wiarygodne wyniki niż organoleptyczne). Prawdopodobnie w teście organoleptycznym użyłeś proxy (lub twoja lokalizacja została źle odczytana) i trafiłeś na jakiegoś CDNa CF który w keszu nie miał twojej strony. GTmetrix dostał pełną obsługę przez CF.

        1. Sprawdzałem na innym nieprzeciążonym komputerze i tam faktycznie było błyskawicznie. Ram na granicy zapełnienia i swap nie sprzyja prędkości.

  6. Super artykuł.

    Potrzebuje jednak informacji na temat współpracy cloudflare i adsense bo bardzo sprzeczne opinie znajduje. Głównie chodzi o ip używkownika serwowane adsom.

    I drugie ssl i adsense.. Jeśli masz wiarygodne informacje na ten temat będę wdzięczny.

    1. Zasoby AdSense tak samo jak GA są zasobami zewnętrznymi, nie „przechodzą” przez CF nie powinno być z nimi żadnego problemu. Google promuje ssl a więc byłoby strzałem w stopę jakby AS miał z nim problemy.

  7. Przy optymalizowaniu subdomeny zauważyłem, że wpisy z .htaccess są brane z pliku znajdującego się w głównym folderze a następnie są do niego dopisywane te z folderu z subdomeną. Przez to subdomena chce pobrać pliki ze strony głównej. Jak to ugryźć w przypadku WP?

    W przypadku stron głównej mam nagłówki:
    ,, i tak dalej.

    W przypadku subdomeny mam:
    ,, … ,,,,,

    1. Chyba obcięło te nagłówki

      W przypadku stron głównej mam nagłówki:
      /wp-content/plugins/conta…7/includes/css/styles.css,/wp-content/plugins/cooki…aw-info/css/cli-style.css,/wp-content/plugins/wp-po…tings/postratings-css.css i tak dalej.

      W przypadku subdomeny mam:
      /wp-content/plugins/conta…7/includes/css/styles.css, /wp-content/plugins/cooki…aw-info/css/cli-style.css,/wp-content/plugins/wp-po…tings/postratings-css.css …

      /styles/style.css,/app.js,/app/controllers/controller.js,/app/directives/bsAccordion.js,/js/ui-bootstrap-accordion-custom-1.3.2.js,/js/ui-bootstrap-accordion-custom-tpls-1.3.2.js

      1. bez dokładnego przyjrzenia się konfiguracji i wpisów niewiele mogę pomóc.
        Tak na szybko co przychodzi mi na myśl a co może być powodem takiego zachowania to że być może redirecty na subdomene w głównym htaccess masz za definicjami doklejającymi linki do nagłówków. W taki wypadku należy przenieść redirecty przed headery.

        1. WordPress w domenie głownej ma defaultowy .htaccess. Doklejanie nagłówków przed i po jego wpisach nic nie zmienia. Definicje dla domeny głównej odpowiadające za server push otoczyłem <Files index.php> … </Files> i wygląda na to, że generują się prawidłowo.

          1. to nie jest dobre rozwiązanie. Zrób testy nie tylko dla strony głównej – zobaczysz dlaczego to rozwiązanie jest do bani (chyba że masz wszystkie adresy w postaci domena\index.php?parametry).
            Tak jak pisałem, nie jestem wróżem, bez wglądu w bebechy strony nie jestem w stanie Ci pomóc.

  8. Nie wiem jak kto jest, ale raz czy dwa znacząco przyspieszyłem stronę swoją lub klienta (np. 2x response time, choć nie za pomocą http/2) i nie zauważyłem zmian w SEO. Wszyscy trąbią, że to ranking factor, są case study, które to potwierdzają, ale mnie jakoś to omija :/

    1. @Jacek
      Lepsza szybkość po prostu nie obniża „średniej wartości strony” ;) . Dziś większość stron jest optymalizowana więc trudno tu o wybicie się dzięki cloudflare.

      A gdzie jest cennik usług ?

  9. Nie dawno była aktualizacja wtyczki cloudflare dla wordpress i wycofują się z HTTP/2 Server Push ?

    „Disabled HTTP/2 Server Push which was leading to 520 and 502 errors for some websites.”

  10. Dla mnie szybkość ładowania strony jest kluczowa w tym momencie, gdyż optymalizuje/odchudzam ją jak mogę. Przy sklepie jednak nie jest to takie proste, ogólnie odpowiedź serwera itp. Czy ktoś może podpowiedzieć jak przejść na http/2 ? Byłbym wdzięczny za poprowadzenie.
    P.s. Dopiero raczkuje w temacie Seo i kwestie techniczne to raczej czarna magia :)

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Back To Top
Przeczytaj poprzedni wpis:
temida-sad-seo
Umowa z firmą SEO … kilka małych kwiatków, na które warto zwrócić uwagę

Podpisując umowę z firmą SEO trzeba bardzo dokładnie i skrupulatnie prześledzić co w tej umowie piszczy. Ja sam niejednokrotnie mam...

krzysztof-marzec-semkrk-2
Relacja z semKRK#2

W krakowskim Pauza in Garden 10 czerwca 2016 po raz drugi odbyło się spotkanie pasjonatów marketingu internetowego pod nazwą semKRK....

Zamknij