Умное Кеширование и Версионность в Javascript/CSS
Подключая внешние CSS и Javascript, мы хотим снизить до минимума лишние HTTP-запросы.
Для этого .js и .css файлы отдаются с заголовками, обеспечивающими надежное кеширование.
Но что делать, когда какой-то из этих файлов меняется в процессе разработки? У всех пользователей в кеше старый вариант - пока кеш не устарел, придет масса жалоб на сломанную интеграцию серверной и клиентской части.
Правильный способ кеширования и версионности полностью избавляет от этой проблемы и обеспечивает надежную, прозрачную синхронизацию версий стиля/скрипта.
Самый простой способ кеширования статических ресурсов - использование ETag .
Достаточно включить соответствующую настройку сервера (для Apache включена по умолчанию) - и к каждому файлу в заголовках будет даваться ETag - хеш, который зависит от времени обновления, размера файла и (на inode-based файловых системах) inode.
Браузер кеширует такой файл и при последующих запросах указывет заголовок If-None-Match с ETag кешированного документа. Получив такой заголовок, сервер может ответить кодом 304 - и тогда документ будет взят из кеша.
Выглядит это так:
- Первый запрос к серверу (кеш чистый)
-
GET /misc/pack.js HTTP/1.1
Host: javascript.ru
Вообще, браузер обычно добавляет еще пачку заголовоков типа User-Agent, Accept и т.п. Для краткости они порезаны.
- Ответ сервера
- Сервер посылает в ответ документ c кодом 200 и ETag:
HTTP/1.x 200 OK
Content-Encoding: gzip
Content-Type: text/javascript; charset=utf-8
Etag: "3272221997"
Accept-Ranges: bytes
Content-Length: 23321
Date: Fri, 02 May 2008 17:22:46 GMT
Server: lighttpd
- Следующий запрос браузера
-
При следующем запросе браузер добавляет
If-None-Match : (кешированный ETag ):
GET /misc/pack.js HTTP/1.1
Host: javascript.ru
If-None-Match: "453700005"
- Ответ сервера
-
Сервер смотрит - ага, документ не изменился. Значит можно выдать код 304 и не посылать документ заново.
HTTP/1.x 304 Not Modified
Content-Encoding: gzip
Etag: "453700005"
Content-Type: text/javascript; charset=utf-8
Accept-Ranges: bytes
Date: Tue, 15 Apr 2008 10:17:11 GMT
Альтернативный вариант - если документ изменился, тогда сервер просто посылает 200 с новым ETag .
Аналогичным образом работает связка Last-Modified + If-Modified-Since :
- сервер посылает дату последней модификации в заголовке
Last-Modified (вместо ETag )
- браузер кеширует документ, и при следующем запросе того же документа посылает дату закешированной версии в заголовке
If-Modified-Since (вместо If-None-Match )
- сервер сверяет даты, и если документ не изменился - высылает только код 304, без содержимого.
Эти способы работают стабильно и хорошо, но браузеру в любом случае приходится делать по запросу для каждого скрипта или стиля.
Общий подход для версионности - в двух словах:
- Во все скрипты добавляется версия (или дата модификации). Например, http://javascript.ru/my.js превратится в http://javascript.ru/my.v1.2.js
- Все скрипты жестко кешируются браузером
- При обновлении скрипта версия меняется на новую: http://javascript.ru/my.v2.0.js
- Адрес изменился, поэтому браузер запросит и закеширует файл заново
- Старая версия 1.2 постепенно выпадет из кеша
Дальше мы разберем, как сделать этот процесс автоматическим и прозрачным.
Жесткое кеширование - своего рода кувалда которая полностью прибивает запросы к серверу для кешированных документов.
Для этого достаточно добавить заголовки Expires и Cache-Control: max-age.
Например, чтобы закешировать на 365 дней в PHP:
header("Expires: ".gmdate("D, d M Y H:i:s", time()+86400*365)." GMT");
header("Cache-Control: max-age="+86400*365);
Или можно закешировать контент надолго, используя mod_header в Apache:
Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT"
Header add "Cache-Control" "max-age=315360000"
Получив такие заголовки, браузер жестко закеширует документ надолго. Все дальнейшие обращения к документу будут напрямую обслуживаться из кеша браузера, без обращения к серверу.
Большинство браузеров (Opera, Internet Explorer 6+, Safari) НЕ кешируют документы, если в адресе есть вопросительный знак, т.к считают их динамическими.
Именно поэтому мы добавляем версию в имя файла. Конечно, с такими адресами приходится использовать решение типа mod_rewrite, мы это рассмотрим дальше в статье.
P.S А вот Firefox кеширует адреса с вопросительными знаками..
Разберем, как автоматически и прозрачно менять версии, не переименовывая при этом сами файлы.
Самое простое - это превратить имя с версией в оригинальное имя файла.
На уровне Apache это можно сделать mod_rewrite:
RewriteEngine on
RewriteRule ^/(.*\.)v[0-9.]+\.(css|js|gif|png|jpg)$ /$1$2 [L]
Такое правило обрабатывает все css/js/gif/png/jpg-файлы, вырезая из имени версию.
Например:
/images/logo.v2.gif -> /images/logo.gif
/css/style.v1.27.css -> /css/style.css
/javascript/script.v6.js -> /javascript/script.js
Но кроме вырезания версии - надо еще добавлять заголовки жесткого кеширования к файлам. Для этого используются директивы mod_header:
Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT"
Header add "Cache-Control" "max-age=315360000"
А все вместе реализует вот такой апачевый конфиг:
RewriteEngine on
# убирает версию, и заодно ставит переменную что файл версионный
RewriteRule ^/(.*\.)v[0-9.]+\.(css|js|gif|png|jpg)$ /$1$2 [L,E=VERSIONED_FILE:1]
# жестко кешируем версионные файлы
Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" env=VERSIONED_FILE
Header add "Cache-Control" "max-age=315360000" env=VERSIONED_FILE
Из-за порядка работы модуля mod_rewrite, RewriteRule нужно поставить в основной конфигурационный файл httpd.conf или в подключаемые к нему(include) файлы, но ни в коем случае не в .htaccess , иначе команды Header будут запущены первыми, до того, как установлена переменная VERSIONED_FILE .
Директивы Header могут быть где угодно, даже в .htaccess - без разницы.
Как ставить версию в имя скрипта - зависит от Вашей шаблонной системы и, вообще, способа добавлять скрипты (стили и т.п.).
Например, при использовании даты модификации в качестве версии и шаблонизатора Smarty - ссылки можно ставить так:
<link href="{version src='/css/group.css'}" rel="stylesheet" type="text/css" />
Функция version добавляет версию:
function smarty_version($args){
$stat = stat($GLOBALS['config']['site_root'].$args['src']);
$version = $stat['mtime'];
echo preg_replace('!\.([a-z]+?)$!', ".v$version.\$1", $args['src']);
}
Результат на странице:
<link href="/css/group.v1234567890.css" rel="stylesheet" type="text/css" />
Чтобы избежать лишних вызовов stat , можно хранить массив со списком текущих версий в отдельной переменной
$versions['css'] = array(
'group.css' => '1.1',
'other.css' => '3.0',
}
В этом случае в HTML просто подставляется текущая версия из массива.
Можно скрестить оба подхода, и выдавать во время разработки версию по дате модификации - для актуальности, а в продакшн - версию из массива, для производительности.
Такой способ кеширования работает везде, включая Javascript, CSS, изображения, flash-ролики и т.п.
Он полезен всегда, когда документ изменяется, но в браузере всегда должна быть текущая актуальная версия.
|
браузеры не кэшируют вопросики только если не переданы заголовки регулирующие кэширование.
.ня
Современная ситуация такова что прокси хуже кешируют вопросики. С браузерами сейчас все в порядке.
Read more here
https://google.com
Try this
https://google.com
https://google.com
https://google.com
google.com
да, и обратная сторона медали - необходимость грохать полностраничный кэш при изменении хотябы одного ресурса. плюс мы не можем экспортировать наши ресурсы на сторонние страницы ( чужие или генерируемые на машинках не имеющих быстрых способов получения актуальных версий ресурсов ).
.ня
Сейчас очень мало ресурсов, где жестко кешируется все HTML-страница.
Но даже если она кешируется по ETag/Last-Modified - то метод замечательно работает.
Конечно, эти ETag/Last-Modified должны обновляться при изменении версии ресурса.
более оптимальное кэширование:
устанавливаем время кэширования в несколько секунд и прячемся за проксёй, которая, соответственно, будет стучаться к нам не чаще чем раз в несколько секунд, а клиенту быстро отдавать 304 или новый контент.
и не надо изобретать велосипед...
.ня
Метод, описанный в этой статье, направлен на то, чтобы полностью избежать лишних запросов с клиента.
В результате страницы грузятся быстрее, т.к меньше запросов.
Этот способ принципиально отличается по характеристикам от того, что ты описал в комментарии, даже сравнивать нельзя.
нифига. в твоём случае после изменения одного ресурса пользователю приходится грузить новую страницу целиком, хотя её контент собственно и не изменился.
в моём - будет загружен только изменившийся ресурс, а вместо страницы будет отдан 304 ответ.
так вот, что быстрее?
1) ревалидировать и загрузить страницу, загрузить ресурс. на каждую загрузку уходит куча хттп-пакетов
2) ревалидировать страницу, ревалидировать все ресурсы, загрузить ресурс. на каждую ревалидацию уходит всего один хттп-пакет
очевидно, что во втором случае пользователь быстрее увидит страницу, если она не изменилась. а вот скорость загрузки изменившегося ресурса в зависимости от ситуации может быть как быстрее, так и медленнее.
ps: ситуацию с сотнями ресурсов я естественно не рассматриваю...
pps: напомню, что все браузеры поддерживают постоянные хттп-соединения...
.ня
Совершенно с тобой согласен. Это разные методы и у них разные характеристики.
Лично я никогда не использую полностраничное кеширование, поэтому даже не стал рассматривать такой случай.
Спасибо за комментарий.
На медленном инете (wifi или gprs) или на просто глючном инете (например, p2p сеть задействовала тысящи запросов и плюс канал забит по максимуму) бывает так, что какой-нть форум загрузится, а css не может, хотя он 100% сидит в кеше. В результате форум отрисован в Times New Roman
. Иногда css таки подгружается через 5-10 секунд, а иногда так и остается.
Способ, описанный в статье, как раз избавляет от этого недуга.
У меня вопрос. я создаю страницу которая периодически обновляется, поэтому кэширование не желательно. но в сранице используется скрипт prototype.js который весит почти 100 кб. в несколько раз больше чем вся страница. можно ли как то на долго кешировать именно этот скрипт, а остальную страницу не кешировать?
Можно пропиши в .htaccess в хэдерах для этого prototype.js Expired большой.
Читай статью.
можно. копай в сторону http headers и .htaccess,
.ня
Прописал этот код на сервере:
RewriteEngine on
# убирает версию, и заодно ставит переменную что файл версионный
RewriteRule ^/(.*\.)v[0-9.]+\.(css|js|gif|png|jpg)$ /$1$2 [L,E=VERSIONED_FILE:1]
# жестко кешируем версионные файлы
Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" env=VERSIONED_FILE
Header add "Cache-Control" "max-age=315360000" env=VERSIONED_FILE
Сервер ругается, что у Header должно быть всего три параметра: действие, заголовок и содержание заголовка. Исполнять не хочет.
У нас получается четыре параметра.
И что делать?
Проверьте версию apache..
Выдержка из http://httpd.apache.org/docs/2.2/mod/mod_headers.html
Syntax: Header [condition] set|append|merge|add|unset|echo|edit header [value] [early|env=[!]variable]
P.S В 2.0 такая запись тоже вроде пашет.
Да вот в том-то и хрень, наверное... У хостера облазил всё - нет намёка на версию...
Только по переданным заголовкам увидел, что Apache 1.3.37...
У него, я так понял, не работает эта штука...
оо... сколько ништяков!
в смысле полезной информации
> Аналогичным образом работает связка Last-Modified + If-Modified-Since
Браво, я бы сказал это самое краткое и понятное описание из того, что читал в сети.
Еще стоит рассмотреть изменение имени по хешу файла и кодирование его в 36-ричной системе (для краткости).
Достоинства:
- "версия" зависит от реального содержимого (т.е. возможен и возврат к старой версии скрипта с использованием старого кэша браузера);
- нет зависимости от времени изменения файла;
- увеличение безопасности (для запроса файла нужно знать его хеш, а не просто имя).
Недостатки:
- при переформатировании скрипта изменится его хеш (но эта ситуация (переформатирование), на мой взгляд, мало вероятна);
- при изменении только скрипта изменится и текст страницы (для исправления ошибок только в скрипте можно предусмотреть специальный механизм, но сомневаюсь, что это сильно нужно).
+10 za statyu...
Все вроде бы так да не так.
Например нажав в firefoxe кнопку reload firefox все равно шлет НА ВСЕ ЭЛЕМЕНТЫ ЗАПРОС забивая на кеширование. В ответ конечно получает 306 но тем не менее зачем то запросы шлет.
Да, так работает релоад в Firefox. А еще можно нажать Ctrl-F5, и он вообще все запросит на 200. Только это ведь релоады. А при хождении по страницам все работает ок.
но и это еще не все.
достаточно закрыть тот же фаерфокс хотя бы на 20 минут. И он опять по новой выгребает ВСЕ.
У меня Жесткое кеширование не работает.
Каждый раз опять загружаются файлы.
Пишу в начале кода
Небольшое исправление в правило:
RewriteRule ^/(.*\.)v[0-9\.]+\.(css|js|gif|png|jpg)$ /$1$2 [L,E=VERSIONED_FILE:1]
(добавлена косая черта '\' перед '.'). Позволит правильно обработать запросы типа my-js.v1.1.js, my-js.v1.1.1.js и т.п.
А статья суперская, все получилось и работает (я на VDS под Linux).
А куда такие header'ы вписывать, если нужно, предположим, js-файл закэшировать? В сам файл с JS?
Можно переименовать .js в .php и добавить в начало скрипта php-код с соответствующими заголовками:
В примере ошибка, нужно использовать именно
max-age=".(86400*365)
вместо
max-age="+86400*365
Доброго времени суток!
Хочу сначала выразить огромную благодарность за проделаную работу на пути к просвитлению тьмы!
Вопрос (возможно я чет не до понял, а скорее всего так и есть):
мне нужно чтобы браузер не кешировал мое изображение в анимации, как это правильно прописать?
А можно еще
href=css/style.css?ver=1.0
При изменении файла css в подключающем файле просто меняется номер версии, и браузер его обновляет без вопросов
А нельзя поменять порядок с помощью order?
Не всегда есть возможность использовать главные конфиг-файлы
У меня стоит следующая задача:
Есть статические js-файлы в хедере страницы. Развернуто веб-приложение из этой одной страницы (html), которое является лишь частью всего большого проекта на ASP.NET.
Необходимо, чтобы работало кеширование без запроса на сервер даже с проверкой модификации. Т.е. чтобы обновить приложение можно было, если почистить кеш браузера. При этом сервер IIS 7, который конфигурировать его нельзя. В ASP.NET тоже вклиниваться нежелательно.
Решение пока не нашел.
народ, может кто подскажет как в ие 8 с помощью javascript чистить кеш?
или может у кого завалялась ссылочка на эту тему ), буду очень признателен
Здравствуйте,
Вы не могли бы подсказать, возможно ли с помощю подобной функции
(может как либо измененной):
function smarty_version($args){
$stat = stat($GLOBALS['config']['site_root'].$args['src']);
$version = $stat['mtime'];
echo preg_replace('!\.([a-z]+?)$!', ".v$version.\$1", $args['src']);
}
дополнить версиями картинки (jpeg, pnp), что на странице?
Или если есть какая то другая?
Буду ОЧЕНЬ благодарен!
Вопрос (возможно я чет не до понял, а скорее всего так и есть): super mario bros
мне нужно чтобы браузер не кешировал мое изображение в анимации, как это правильно прописать?
The information is very special, I will have to follow you, the information you bring is very real, reflecting correctly and objectively, it is very useful for society to grow together. candy crush soda
Your share is the great knowledge I have gathered, you are an important person I admire, thank you happy wheels
Your share is the great knowledge I have gathered, you are an important person I admire, thank you basketball legends
Maybe it will be better, I need to find out and need more information krunker
This content is written very well. Your use of formatting when making your points makes your observations very clear and easy to understand. Thank you.
https://arkserverhosting.co.uk
I admire this article for the well-researched content and excellent wording. I got so involved in this material that I couldn’t stop reading. I am impressed with your work and skill. Thank you so much.
portable hairdryer
Thanks so much for this information. I have to let you know I concur on several of the points you make here and others may require some further review, but I can see your viewpoint.
crime scene cleanup
Awesome article! I want people to know just how good this information is in your article. It’s interesting, compelling content. Your views are much like my own concerning this subject.
mini carpet cleaner
I am happy to find this post very useful for me, as it contains lot of information. I always prefer to read the quality content and this thing I found in you post. Thanks for sharing.
trulylovelykitchen
Thank you for very usefull information..
This is the type of information I’ve long been trying to find. Thank you for writing this information.
婚約指輪 福岡
The information you have posted is very useful. The sites you have referred was good. Thanks for sharing...
エンゲージリング
I can set up my new idea from this post. It gives in depth information. Thanks for this valuable information for all,..
婚約指輪 猫
Very good written article. It will be supportive to anyone who utilizes it, including me. Keep doing what you are doing – can’t wait to read more posts.
婚約指輪 福岡
We hope that our list of popular scams on Craigslist Las Vegas NV has helped understand what to watch out for when contacting a seller. Before we move on, however, we have a few more tips to you. To begin with, if you are planning to meet some from Craigslist in Las Vegas, you should always try to make sure that the encounter would be in a public place.
Hello There. I found your blog using msn. This is an extremely well written article. I will be sure to bookmark it and return to read more of your useful information. Thanks for the post. I’ll certainly comeback.
PEMF
Good website! I truly love how it is easy on my eyes it is. I am wondering how I might be notified whenever a new post has been made. I have subscribed to your RSS which may do the trick? Have a great day!
pemf therapy
Good post but I was wondering if you could write a litter more on this subject? I’d be very thankful if you could elaborate a little bit further. Appreciate it!
post free ads
This was really an interesting topic and I kinda agree with what you have mentioned here!
depannage rideau metallique
Most of the time I don’t make comments on websites, but I'd like to say that this article really forced me to do so. Really nice post!
depannage rideau metallique
I have bookmarked your blog, the articles are way better than other similar blogs.. thanks for a great blog!
click here
Your website is really cool and this is a great inspiring article.
Modbus RTU gateway
I have read your article, it is very informative and helpful for me. I admire the valuable information you provide in your articles. Thank you for posting it. bmi calculator
I really appreciate the kind of topics you post here. Thanks for sharing us a great information that is actually helpful. Good day!
I have not any word to appreciate this post.....Really i am impressed from this post....the person who create this post it was a great human..thanks for shared this with us.
click here
Superbly written article, if only all bloggers offered the same content as you, the internet would be a far better place..
click here
Your website is really cool and this is a great inspiring article.
Cryptocurrency Price Widget
Thanks for sharing this information. I really like your blog post very much. You have really shared a informative and interesting blog post with people..
find here
Hard workers never need despair, because everything is achieved by hard work and hard work. I feel great when you know more about find a word
I just found this blog and have high hopes for it to continue. Keep up the great work, its hard to find good ones. I have added to my favorites. Thank You.
Estimate
Thanks for taking the time to discuss this, I feel strongly about it and love learning more on this topic.
seo
Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
carpet cleaning
Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
homeostasis
Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
immigration attorney
I know your expertise on this. I must say we should have an online discussion on this. Writing only comments will close the discussion straight away! And will restrict the benefits from this information.
Akıllı Ev Sistemleri
I can see that you are an expert at your field! I am launching a website soon, and your information will be very useful for me.. Thanks for all your help and wishing you all the success in your business.
land clearing
I recently came across your blog and have been reading along. I thought I would leave my first comment. I don't know what to say except that I have enjoyed reading. Nice blog. I will keep visiting this blog very often.
tra thao moc giam can vy tea
I know your expertise on this. I must say we should have an online discussion on this. Writing only comments will close the discussion straight away! And will restrict the benefits from this information.
fathers day cards
Really a great addition. I have read this marvelous post. Thanks for sharing information about it. I really like that. Thanks so lot for your convene.
cut files
Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
Ipswich Gutter cleaning
I can see that you are an expert at your field! I am launching a website soon, and your information will be very useful for me.. Thanks for all your help and wishing you all the success in your business.
drone video services
Thanks for spending the time to debate this, I really feel strongly about it and love studying more on this topic. Avail now our bricklaying service.
Really a great addition. I have read this marvelous post. Thanks for sharing information about it. I really like that. Thanks so lot for your convene.
product videos
Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
accident lawyer
I haven’t any word to appreciate this post.....Really i am impressed from this post....the person who create this post it was a great human..thanks for shared this with us.
Retro spellen kopen
I wish more authors of this type of content would take the time you did to research and write so well. I am very impressed with your vision and insight.
accident attorney
Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..gut health
Really a great addition. I have read this marvelous post. Thanks for sharing information about it. I really like that. Thanks so lot for your convene.
anunturi gratuite angajari
I know your expertise on this. I must say we should have an online discussion on this. Writing only comments will close the discussion straight away! And will restrict the benefits from this information.
Harley-Davidson Motorclothes
I can see that you are an expert at your field! I am launching a website soon, and your information will be very useful for me.. Thanks for all your help and wishing you all the success in your business.
Automatisierung im Handwerk
This is very educational content and written well for a change. It's nice to see that some people still understand how to write a quality post.!
fake grass Sheffield
I really loved reading your blog. It was very well authored and easy to undertand. Unlike additional blogs I have read which are really not tht good. I also found your posts very interesting. In fact after reading, I had to go show it to my friend and he ejoyed it as well!
tilers in Leeds
Thank you so much for sharing this great blog.Very inspiring and helpful too.Hope you continue to share more of your ideas.I will definitely love to read.
photo booth sale
Nice blog and absolutely outstanding. You can do something much better but i still say this perfect.Keep trying for the best.
photo booth sale
This is highly information, crisp and clear. I think that everything has been described in systematic manner so that reader could get maximum information and learn many things.
3d printing Manchester
I think this is one of the most significant information for me. And i’m glad reading your article. But should remark on some general things, The web site style is perfect, the articles is really great : D. Good job, cheers
printing derby
This type of message always inspiring and I prefer to read quality content, so happy to find good place to many here in the post, the writing is just great, thanks for the post.
roofing Lancaster
This is my first time visit here. From the tons of comments on your articles,I guess I am not only one having all the enjoyment right here!
car repair Reading
I am happy to find this post very useful for me, as it contains lot of information. I always prefer to read the quality content and this thing I found in you post. Thanks for sharing.
hogar inteligente
This is very interesting content! I have thoroughly enjoyed reading your points and have come to the conclusion that you are right about many of them. You are great.
mobile mechanic Reading
This was a really great contest and hopefully I can attend the next one. It was alot of fun and I really enjoyed myself..
Wakefield pest control
I really like reading through a post that can make people think. Also, many thanks for permitting me to comment! 8 ball
Very nice article, I enjoyed reading your post, very nice share, I want to twit this to my followers. Thanks!.
maison intelligente
This is just the information I am finding everywhere. Thanks for your blog, I just subscribe your blog. This is a nice blog..
Click here
Very good points you wrote here..Great stuff...I think you've made some truly interesting points.Keep up the good work.
read more
Thanks for posting this info. I just want to let you know that I just check out your site and I find it very interesting and informative. I can't wait to read lots of your posts.
instagram Abonnenten kaufen bei abonnenten24.de
I am very enjoyed for this blog. Its an informative topic. It help me very much to solve some problems. Its opportunity are so fantastic and working style so speedy.
Domótica
Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
abonnenten24
It’s hard to come by experienced people in this particular topic,but you sound like you know what you’re talking about! Thanks temple run
I’ve been searching for some decent stuff on the subject and haven't had any luck up until this point, You just got a new biggest fan!..abonnenten24
I can see that you are an expert at your field! I am launching a website soon, and your information will be very useful for me.. Thanks for all your help and wishing you all the success in your business.
digital marketing
Awesome blog. I enjoyed reading your articles. This is truly a great read for me. I have bookmarked it and I am looking forward to reading new articles. Keep up the good work!
digital marketing
I can see that you are an expert at your field! I am launching a website soon, and your information will be very useful for me.. Thanks for all your help and wishing you all the success in your business.
alquiler de carros en cartagena
Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
bridgend house clearance
I can see that you are an expert at your field! I am launching a website soon, and your information will be very useful for me.. Thanks for all your help and wishing you all the success in your business.
bridgend house clearance
Very nice article, I enjoyed reading your post, very nice share, I want to twit this to my followers. Thanks!.
hartlepool removals
Great write-up, I am a big believer in commenting on blogs to inform the blog writers know that they’ve added something worthwhile to the world wide web!..
Heimautomation
I can see that you are an expert at your field! I am launching a website soon, and your information will be very useful for me.. Thanks for all your help and wishing you all the success in your business.
tree surgeon in colchester
Отправить комментарий
Приветствуются комментарии:Для остальных вопросов и обсуждений есть форум.