<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Web Scaling Blog &#187; Nail</title>
	<atom:link href="http://www.webscalingblog.com/author/nail/feed" rel="self" type="application/rss+xml" />
	<link>http://www.webscalingblog.com</link>
	<description>Everything about web scaling and high availability</description>
	<lastBuildDate>Fri, 21 May 2010 13:31:12 +0000</lastBuildDate>
	
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>IE and DOMReady bug</title>
		<link>http://www.webscalingblog.com/uncategorized/ie-and-domready.html</link>
		<comments>http://www.webscalingblog.com/uncategorized/ie-and-domready.html#comments</comments>
		<pubDate>Thu, 22 Jan 2009 08:19:13 +0000</pubDate>
		<dc:creator>Nail</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.webscalingblog.com/?p=105</guid>
		<description><![CDATA[Most famous method to catch DOMReady event in IE6/7 is utilizing script defer attribute:
/*@cc_on @*/
/*@if (@_win32)
document.write("&#60;script id=__ie_onload defer src=javascript:void(0)&#62;&#60;\/script&#62;");
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
&#160;&#160;if (this.readyState == "complete") {
&#160;&#160;&#160;&#160;init(); // call the onload handler
  }
};
/*@end @*/
I&#8217;m developing a performance measurement tool and took this code to record time to DOMReady. Testing on a real [...]]]></description>
			<content:encoded><![CDATA[<p>Most famous method to catch DOMReady event in IE6/7 is <a href="http://dean.edwards.name/weblog/2006/06/again/">utilizing script defer attribute</a>:<br />
<code>/*@cc_on @*/<br />
/*@if (@_win32)<br />
document.write("&lt;script id=__ie_onload defer src=javascript:void(0)&gt;&lt;\/script&gt;");<br />
var script = document.getElementById("__ie_onload");<br />
script.onreadystatechange = function() {<br />
&nbsp;&nbsp;if (this.readyState == "complete") {<br />
&nbsp;&nbsp;&nbsp;&nbsp;init(); // call the onload handler<br />
  }<br />
};<br />
/*@end @*/</code></p>
<p>I&#8217;m developing a performance measurement tool and took this code to record time to DOMReady. Testing on a real site I&#8217;ve discovered that many records come with zero time to DOMReady. Filtering the records by browser I&#8217;ve got that only IE&#8217;s records were zero.<br />
This zero time means that the event fired right after it was defined. This means that in practice on IE we could run the code bound to DOMReady before actually DOM Tree is ready.<br />
<a href="http://blogs.atlassian.com/developer/2008/03/when_ie_says_dom_is_ready_but.html">There are similar issues described here</a>.</p>
<p>After investigation I could reproduce it and found out that one of third-party advertisements caused the bug. It was inside IFRAME and it had alot of Javascript that was doing document.write() plugging next Javascript etc.</p>
<p><strong>How to fix it?</strong><br />
There could be different solutions:</p>
<ul>
<li> if possible, move the code to the bottom of the page instead of using DOMReady event
<li> move the code into window.onload (execution will be delayed)
<li> use another implementation of DOMReady for IE, there is <a href="http://javascript.nwbox.com/IEContentLoaded/">solution</a> that utilizes &#8220;doScroll()&#8221; method in order to check whether the content is ready yet
</ul>
<p>Note that it might be not so easy to find this bug. Your Javascript application that starts in DOMReady will work perfectly with your browser on your development server, but on production it may fail because of advertisements enabled and in worse scenario you&#8217;ll get geo-targeted advertisements and the bug will appear only for several countries.</p>
<p><strong>How to catch it?</strong><br />
Measure the time in Javascript and send alert if the code runs right after event binding:<br />
<code><br />
var page_start = new Date().getTime();<br />
function bootstrap()<br />
{<br />
&nbsp;&nbsp;if (0 == (new Date().getTime() - page_start))<br />
&nbsp;&nbsp;&nbsp;&nbsp;new Image().src="/developer_alert.php"; // send alert<br />
...<br />
...<br />
}<br />
bind_domready(bootstrap);<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.webscalingblog.com/uncategorized/ie-and-domready.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Automated Data:URI CSS Sprites</title>
		<link>http://www.webscalingblog.com/uncategorized/automated-datauri-css-sprites.html</link>
		<comments>http://www.webscalingblog.com/uncategorized/automated-datauri-css-sprites.html#comments</comments>
		<pubDate>Sun, 14 Dec 2008 05:57:48 +0000</pubDate>
		<dc:creator>Nail</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.webscalingblog.com/?p=100</guid>
		<description><![CDATA[Recently I found another interesting service: DURIS (Data URI Sprites). This is an automated solution for building Data:URI CSS Sprites for background images.
Basically you provide a page URL, then the service loads all styles of the page (both internal and external), finds all background images and encodes them into base64 (for data:URI format). As output, [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I found another interesting service: <a href="http://duris.ru">DURIS</a> (Data URI Sprites). This is an automated solution for building Data:URI CSS Sprites for background images.</p>
<p>Basically you provide a page URL, then the service loads all styles of the page (both internal and external), finds all background images and encodes them into base64 (for data:URI format). As output, it provides a tar.gz file with HTML file and CSS files with encoded images in textual format.</p>
<p>The HTML contains the original pages with built-in code that checks browser version and plugs the styles with encoded images. There are several version of the styles for different browsers:</p>
<ul>
<li>MHTML &#8211; for IE version < 8 (only IE8 supports data:URI )</li>
<li>data:URI &#8211; for all the other browsers</li>
<li>original styles &#8211; for the case when Javascript is disabled and for IE7 under Vista (there is a bug related to security issue with cached MHTML background images)</li>
</ul>
<p>The advantages are:</p>
<ul>
<li>all background images will be loaded in one HTTP request</li>
<li>it&#8217;s possible to quickly rebuild CSS sprites after making changes</li>
</ul>
<p>This is a new tool that&#8217;s in beta stage so I would not use it in production.<br />
The tool written in Java and the authors are going to opensource it as soon as release-candidate is ready.<br />
(via <a href="http://habrahabr.ru/blogs/webdev/46801/">Habr article</a> in Russian)</p>
<p>I remind that besides Data:URI Sprites we have classic Image Sprites that involve joining images into another big image (opposite to base64 supposed by Data:URI). There are number of tools exist for Image Sprites and most advanced that I know is <a href="http://smartsprites.osinski.name/">SmartSprites</a>.</p>
<p>The advantage of Image Sprites over Data:URI Sprites is that it&#8217;s supported by almost all browsers including very old versions. The disadvantage is that Image Sprites composition cannot be fully automated and every time should be controlled by web developer.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.webscalingblog.com/uncategorized/automated-datauri-css-sprites.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Website Speed Optimization and Waterfall Diagram</title>
		<link>http://www.webscalingblog.com/performance/website-speed-optimization-and-waterfall-diagram.html</link>
		<comments>http://www.webscalingblog.com/performance/website-speed-optimization-and-waterfall-diagram.html#comments</comments>
		<pubDate>Mon, 10 Nov 2008 11:32:12 +0000</pubDate>
		<dc:creator>Nail</dc:creator>
				<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.webscalingblog.com/?p=71</guid>
		<description><![CDATA[Waterfall diagram is useful to find out how browser spending time loading page components. There is a an opensource tool built for IE called AOL Pagetest. It has online version where we can check sites via connections in USA and we can define connection speed.
I&#8217;ve prepared a sample page: http://webscalingblog.com/waterfall/1/, let&#8217;s look how we can [...]]]></description>
			<content:encoded><![CDATA[<p>Waterfall diagram is useful to find out how browser spending time loading page components. There is a an opensource tool built for IE called <a href="http://pagetest.wiki.sourceforge.net/">AOL Pagetest</a>. It has <a href="http://performance.webpagetest.org:8080/">online version</a> where we can check sites via connections in USA and we can define connection speed.</p>
<p>I&#8217;ve prepared a sample page: <a href="http://webscalingblog.com/waterfall/1/">http://webscalingblog.com/waterfall/1/</a>, let&#8217;s look how we can optimize it.</p>
<p>Waterfall diagram of loading this page using IE7 (clickable):<br />
<a href="http://webscalingblog.com/waterfall/img/waterfall1-ie.png" target="_blank"><img src="http://webscalingblog.com/waterfall/img/waterfall1-ie-preview.png" alt="" /></a><br />
<span id="more-71"></span></p>
<p>We see that page contains 3 CSS components, 3 Javascript and 7 images.<br />
There are two orange spot on the diagram &#8211; the browser opened two connections and was ready to download two components in parallel.<br />
Green part of bars &#8211; time to first byte &#8211; it&#8217;s the HTTP request overhead. The overhead here is 150-200ms on each HTTP request. We see much of green color on the diagram.</p>
<p>Look at 2.css, 2.js and 3.js &#8211; the browser spent most of time sending request and waiting rather than downloading.</p>
<p>Especially important is Time to Start Render which is shown as green vertical line. We see that it took 2 seconds to start render this pretty simple page.<br />
2 seconds the browser downloaded HTML, CSS and JS components.</p>
<p>As first step I&#8217;ve joined the CSS files and the JS files into 1 CSS file and 1 JS file:<br />
<a href="http://webscalingblog.com/waterfall/2/">http://webscalingblog.com/waterfall/2/</a></p>
<p><a href="http://webscalingblog.com/waterfall/img/waterfall2-ie.png" target="_blank"><img src="http://webscalingblog.com/waterfall/img/waterfall2-ie-preview.png" alt="" /></a></p>
<p>We see it gets better but browser still spending time on the Javascript, rendering cannot be started until browser downloaded and executed Javascript. And Javascript is blocking downloading of next components.</p>
<p>Next example shows what we get after moving Javascript to the bottom of page (let&#8217;s suppose that in this case we can do it).</p>
<p><a href="http://webscalingblog.com/waterfall/3/">http://webscalingblog.com/waterfall/3/</a></p>
<p><a href="http://webscalingblog.com/waterfall/img/waterfall3-ie.png" target="_blank"><img src="http://webscalingblog.com/waterfall/img/waterfall3-ie-preview.png" alt="" /></a></p>
<p>It becomes pretty much better, Time to Start Rendering was improved. Javascript is loading after the all other components loaded.<br />
But still, the browser has to download CSS, there is green bar (wait time) after HTML downloading is done and the CSS component consumes one downloading slot.</p>
<p>Now let&#8217;s try to embed CSS into HTML.</p>
<p><a href="http://webscalingblog.com/waterfall/4/">http://webscalingblog.com/waterfall/4/</a></p>
<p><a href="http://webscalingblog.com/waterfall/img/waterfall4-ie.png" target="_blank"><img src="http://webscalingblog.com/waterfall/img/waterfall4-ie-preview.png" alt="" /></a></p>
<p>Finally, Time to Start Render was reduced dramatically. Now it&#8217;s 0.8s instead of 2s we had at the beginning.</p>
<p>Note that after embeding CSS we increased download volume for cached page views, because browser now has to download CSS within HTML. To avoid this, we can use the technique with cookie to guess cache state. It was described in <a href="http://www.webscalingblog.com/performance/stylesheet-composition.html">the previous post</a>.</p>
<p>Going from waterfall3 to waterfall4 we didn&#8217;t improve Full Load Time. So if we would use some benchmark to measure Full Load Time we probably would not notice any difference. While from end-user point of view there was significant improvement in response time.</p>
<p>At the moment I don&#8217;t know any benchmark (besides AOL Pagetest) that&#8217;s able to measure Time to Start Render. There is <a href="http://getfirebug.com/releases/firebug/1.3/">Firebug 1.3 Beta</a> for Firefox which is able to do waterfall diagrams and which shows time to DOMContentReady and most time we can use it, but actually it isn&#8217;t the same, I noticed that moving Javascript to the bottom doesn&#8217;t improve DOMContentReady time while actually browser starts rendering earlier (I tested it with a big Javascript file)</p>
<p>And last step, if we want to improve Full Load Time and if we can use CSS Image Sprites here, we can join images into one bigger image.</p>
<p><a href="http://webscalingblog.com/waterfall/5/">http://webscalingblog.com/waterfall/5/</a><br />
(Sorry, I&#8217;ve simply included the file and didn&#8217;t use CSS positioning)</p>
<p><a href="http://webscalingblog.com/waterfall/img/waterfall5-ie.png" target="_blank"><img src="http://webscalingblog.com/waterfall/waterfall5-ie-preview.png" alt="" /></a></p>
<p>It reduced Full Load Time from 2.4s to 2s. And we had only 7 images &#8211; usually sites contain much more and improvement will be more significant.<br />
<a href="http://www.webscalingblog.com/performance/using-css-image-sprites.html">CSS Sprites were described in one of previous posts</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.webscalingblog.com/performance/website-speed-optimization-and-waterfall-diagram.html/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Stylesheet Composition</title>
		<link>http://www.webscalingblog.com/performance/stylesheet-composition.html</link>
		<comments>http://www.webscalingblog.com/performance/stylesheet-composition.html#comments</comments>
		<pubDate>Thu, 30 Oct 2008 20:08:25 +0000</pubDate>
		<dc:creator>Nail</dc:creator>
				<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.webscalingblog.com/?p=30</guid>
		<description><![CDATA[Browsers cannot start rendering till all Stylesheets loaded. Visitors see blank page (or previous page) during loading of CSS so it&#8217;s especially important to load these components faster.
If you have many Stylesheet components, then visitors will have to wait more because of HTTP requests overhead.
Let&#8217;s look what can be done to reduce time to render.

1. [...]]]></description>
			<content:encoded><![CDATA[<p>Browsers cannot start rendering till all Stylesheets loaded. Visitors see blank page (or previous page) during loading of CSS so it&#8217;s especially important to load these components faster.<br />
If you have many Stylesheet components, then visitors will have to wait more because of HTTP requests overhead.<br />
Let&#8217;s look what can be done to reduce time to render.<br />
<span id="more-30"></span><br />
<strong>1.</strong> First, we want to move CSS components to the HEAD section of HTML document so the Stylesheet will be loaded with first priority.<br />
Worse case is when CSS included on the bottom &#8211; in this case browser waits till whole document including all components loaded before start rendering. The same thing may happen when CSS included with @import directive, so avoid using @import.</p>
<p><strong>2.</strong> If we have several Stylesheets we join them into one bigger CSS file. As described in the previous post about <a href="http://www.webscalingblog.com/performance/using-css-image-sprites.html">Image Sprites</a>, we want to reduce amount of downloaded components because of HTTP request overhead. And if in case of images there is just a delay before loading next image, in case of Stylesheet it&#8217;s a worse case: visitors see blank page until all Stylesheets loaded. One single file loads faster because there will be minimal overhead and usually it compresses better too.</p>
<p><strong>3.</strong> We can go even further: if user comes with empty cache we can avoid additional request by including Stylesheet required for the page into HTML. So there will be no requests for CSS at all during loading of the page.<br />
We can preload Stylesheet after the page loaded so for primed cache page views user already has the Stylesheet cached. This way we reduce download size and avoid HTTP requests.<br />
More detailed:<br />
Set a cookie when visitor comes for first time. Check the cookie on every request to determine whether the visitor comes first time or not. This way we can guess cache state of the visitor. Don&#8217;t inline Stylesheet component if visitor comes with primed cache, use an external definition instead.<br />
Postload the external Stylesheet component when visitor comes first time (without the cookie). See example below.<br />
Cookie named &#8220;jsl&#8221; will be set when user sees this page for first time and next requests will use external Stylesheet component.<br />
You can set such cookie in PHP:<br />
<code><br />
$is_first = !isset($_COOKIE['jsl']);<br />
if ($is_first)<br />
  setcookie('jsl',1,time()+3600*24,'/','.site.com',false,true);<br />
</code><br />
We&#8217;ve supposed that browser cache data will be removed in 24 hours and cookie expiration time set to 24 hours.</p>
<p><code><br />
&lt;?php if ($is_first): ?&gt;<br />
&lt;style&gt;<br />
&lt;?php readfile('style.css') ?&gt;<br />
&lt;/style&gt;<br />
&lt;script&gt;<br />
function preload_components()<br />
{<br />
var ste = document.createElement('link');<br />
ste.rel = 'stylesheet';<br />
ste.type = 'text/css';<br />
ste.href = 'http://www.site.com/style.css';<br />
document.getElementsByTagName('head')[0].appendChild(ste);<br />
}<br />
&lt;/script&gt;<br />
&lt;?php else: ?&gt;<br />
&lt;link rel="stylesheet" type="text/css" href="style.css"&gt;<br />
&lt;?php endif ?&gt;<br />
</code></p>
<p>The block above will output inline Stylesheet in case user comes for first time otherwise it will output link to the static Stylesheet components. You should place this code inside document head.<br />
It will work correctly even in case user comes with empty cache (for example if our cache got forced out because cache size limit).</p>
<p>Style.css &#8211; global CSS file. Probably in real application instead of readfile(&#8217;style.css&#8217;) you will include only part of the Stylesheet that&#8217;s needed to render current page.<br />
To find out CSS that&#8217;s really required for current page, you can use a Firefox extension: <a href="http://www.sitepoint.com/dustmeselectors/">Dust-Me Selectors</a> (note that this tool may not count CSS rules for elements you assign dynamically in Javascript).</p>
<p>Replace existing opening <body> tag<br />
<code>&lt;body …&gt;</code><br />
with<br />
<code>&lt;body … &lt;?php if ($is_first) echo 'onload="preload_components()"'?&gt;&gt;</code><br />
We&#8217;ve added document onload event handler that will create link DOM element if user comes with empty cache so the components will be already in cache for further page views. The external component will be preloaded only after whole page loaded and rendered so it won’t hurt performance.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.webscalingblog.com/performance/stylesheet-composition.html/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Caching HTTP Headers, Last-Modified and ETag</title>
		<link>http://www.webscalingblog.com/performance/caching-http-headers-last-modified-and-etag.html</link>
		<comments>http://www.webscalingblog.com/performance/caching-http-headers-last-modified-and-etag.html#comments</comments>
		<pubDate>Sun, 12 Oct 2008 06:58:16 +0000</pubDate>
		<dc:creator>Nail</dc:creator>
				<category><![CDATA[Caching]]></category>
		<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.webscalingblog.com/?p=13</guid>
		<description><![CDATA[What if we cannot predict lifetime of page content? If we have a page with info that changes unpredictably we still can use browser cache to avoid unneeded traffic.
Using validation mechanism browser sends HTTP request with info about cache entry and server can respond that the content wasn&#8217;t changed.
There are two validation methods: one is [...]]]></description>
			<content:encoded><![CDATA[<p>What if we cannot predict lifetime of page content? If we have a page with info that changes unpredictably we still can use browser cache to avoid unneeded traffic.<br />
Using validation mechanism browser sends HTTP request with info about cache entry and server can respond that the content wasn&#8217;t changed.<br />
There are two validation methods: one is based on Last-Modified and the other is based on Etag.<br />
<span id="more-13"></span><br />
<strong>Last-Modified</strong><br />
Server sends <strong>Last-Modified</strong> header with datetime value that means the time when content was changed last time.<br />
<code>Cache-Control: must-revalidate<br />
Last-Modified: 15 Sep 2008 17:43:00 GMT<br />
</code></p>
<p>The first header <strong>Cache-Control: must-revalidate</strong> means that browser must send validation request every time even if there is already cache entry exists for this object.<br />
Browser receives the content and stores it in the cache along with the last modified value.<br />
Next time browser will send additional header:<br />
<code>If-Modified-Since: 15 Sep 2008 17:43:00 GMT</code></p>
<p>This header means that browser has cache entry that was last changed 17:43.<br />
Then server will compare the time with last modified time of actual content and if it was changed server will send the whole updated object along with new Last-Modified value.</p>
<p>If there were no changes since the previous request then there will be short empty-body answer:<br />
<code>HTTP/1.x 304 Not Modified</code></p>
<p>And browser will use the cached content.</p>
<p>What if server doesn&#8217;t send <strong>Cache-Control: must-revalidate</strong>?<br />
Then modern browsers look at profile setting or decide on their own whether to send conditional request. So we better to send Cache-Control to make sure that browser sends conditional request.</p>
<p>Sample PHP code:<br />
<code><br />
$last_modified_ts = floor(mktime()/30)*30;<br />
if (<br />
    isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &#038;&#038;<br />
    strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $last_modified_ts<br />
   )<br />
{<br />
  header('HTTP/1.1 304 Not Modified');<br />
  exit;<br />
}<br />
header('Cache-Control: must-revalidate');<br />
header('Last-Modified: '.gmdate('d M Y H:i:s',$last_modified_ts).' GMT');<br />
echo date('d M Y H:i:s');<br />
</code></p>
<p>This example outputs cached datetime that&#8217;s expiring every 30 seconds.</p>
<p>Last-Modified suits good in case we can easily calculate modification time of page content.</p>
<p>We must be careful with Last-Modified if we have page content that consists many fragments. We should calculate Last-Modified value of every fragment and get the latest one.<br />
Note that if we have authentication and there is a page fragment that depends on authentication we have to reset Last-Modified value after login/logout &#8211; for every page that contains the fragment.<br />
Also note that in case of several web servers we should make sure that Last-Modified value changes synchronous for all the servers.</p>
<p><strong>ETag</strong><br />
This method suits for cases when it&#8217;s difficult to maintain Last-Modified value: when you have complicated application with many page fragments especially if there are third-party libraries. Or for the case with authentication, when page content depends on authentication info.<br />
There is simple idea besides <strong>ETag</strong>:<br />
ETag value depends on the content and must be different for different content and the same for the same content.</p>
<p>Sample usage of ETag header:<br />
<code>$content = floor(mktime()/30)*30;<br />
$etag = md5($content);<br />
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) &#038;&#038; $_SERVER['HTTP_IF_NONE_MATCH'] == $etag)<br />
{<br />
  header('HTTP/1.1 304 Not Modified');<br />
  exit;<br />
}<br />
header('Cache-Control: must-revalidate');<br />
header('ETag: '.$etag);<br />
echo $content;<br />
echo '<br />Request time: '.date('d M Y H:i:s');<br />
</code></p>
<p>In this example content changes every 30 seconds and browsers will download only if the content was changed.</p>
<p><strong>Static Content and Unnecessary ETag Header</strong><br />
For static content it&#8217;s recommended to send <strong>Cache-Control: max-age=&#8230;</strong> header with higher max-age value. In this case browser won&#8217;t send any request on normal page views.<br />
So for static content there is no use of ETag header.<br />
The worse case is in case of web servers cluster as ETag value differs for the file on different servers.<br />
For Lighttpd server you can disable Etag using<br />
<code>static-file.etags = 'disable'</code><br />
in lighttpd.conf</p>
<p>Disabling ETag in Apache:<br />
<code>Header unset ETag<br />
FileETag None</code></p>
<p>Note that we still want <strong>Last-Modified</strong> header for static files. If user presses Refresh button, then browser will send conditional request and server will respond “304 Not Modified”.<br />
And if you disable both Last-Modified and ETag browser will have to download the whole content again when user presses Refresh.</p>
<p>Lighttpd and Apache will send Last-Modified if you have configured mod_expires.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.webscalingblog.com/performance/caching-http-headers-last-modified-and-etag.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Using CSS Image Sprites</title>
		<link>http://www.webscalingblog.com/performance/using-css-image-sprites.html</link>
		<comments>http://www.webscalingblog.com/performance/using-css-image-sprites.html#comments</comments>
		<pubDate>Mon, 22 Sep 2008 17:24:35 +0000</pubDate>
		<dc:creator>Nail</dc:creator>
				<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.webscalingblog.com/?p=20</guid>
		<description><![CDATA[Even fast connections suffer from multiple HTTP request overhead because of network latency and HTTP request processing overhead.
MSIE7 opens no more than 2 connections per domain, Firefox3 &#8211; 4-6 connections, Google Chrome &#8211; 6 connections.
Let&#8217;s suppose that time to first byte is 70ms (typical response time of http://google.com via 1.5 Mbps ADSL connection from Dulles)
If [...]]]></description>
			<content:encoded><![CDATA[<p>Even fast connections suffer from multiple HTTP request overhead because of network latency and HTTP request processing overhead.<br />
MSIE7 opens no more than 2 connections per domain, Firefox3 &#8211; 4-6 connections, Google Chrome &#8211; 6 connections.<br />
Let&#8217;s suppose that time to first byte is 70ms (typical response time of http://google.com via 1.5 Mbps ADSL connection from Dulles)<br />
If a page view involves loading of 20 images from the same domain, then total overhead in MSIE7 is 20/2*70ms = 700ms. The more images site contains, the more time visitor will have to wait.<br />
<span id="more-20"></span><br />
The popular solution is to use <a href="http://alistapart.com/articles/sprites">CSS Image Sprites</a>:<br />
Join design/logo images into one bigger image. Use spriting technique (css background) to show the images on page. An image can be defined via css property <code>background-position</code>.<br />
This approach is compatible with modern browsers (and even with IE4).</p>
<p>Let&#8217;s review the process step by step:<br />
<strong>Step1.</strong><br />
Replace image entries <code>&lt;img src="/img/anyimage.gif"&gt;</code> with <code>&lt;div  class="i_anyimage"&gt;&lt;/div&gt;</code>.</p>
<p>Add new css entry:<br />
<code>.i_anyimage<br />
{<br />
  width: 17px;<br />
  height: 17px;<br />
  background-repeat: no-repeat;<br />
  background-image: url('/img/anyimage.gif');<br />
}</code><br />
At the end you should get your pages fully functional and identical to the originals.</p>
<p><strong>Step2.</strong><br />
Now you should join all the images into one bigger file. You can use an image editor or create automated script, etc.</p>
<p>Once the image is ready you start to change the css entries so they use the sprite image instead of original image:<br />
<code>.i_anyimage<br />
{<br />
  width: 17px;<br />
  height: 17px;<br />
  background-repeat: no-repeat;<br />
  background-image: url('/img/sprite.png');<br />
  background-position:  -XXpx -YYpx;<br />
}<br />
</code><br />
XX, YY are coordinates of the original image within the sprite.</p>
<p>What can be useful to know here:</p>
<li>Image compression format &#8211; which is better? I recommend to use 8-bit PNG format for non-photo images when it&#8217;s possible and JPEG format for photo images.<br />
Some images can be saved in PNG8 without visible loss of quality and it makes sense to do so since this will reduce size of file by about 50% comparing to truecolor PNG. PNG8 also compresses better than GIF and PNG8 supports GIF-like transparency. Size of PNG8 file is 20% less than GIF in average.
</li>
<li>Make sure your PNG compressed well. There is a number of PNG compression tools, a good example is command-line tool <a href="http://pmt.sourceforge.net/pngcrush/ ">pngcrush</a>.<br />
You can compress PNG using this command:<br />
<code>pngcrush -rem alla -brute -reduce src.png dest.png</code>
</li>
<li>There can be complicated positioning case if your site already using background images that are aligned to the right side or to the bottom of element. In this case you should take horizontal positioning of image chain for aligning to the bottom (and the bottom-aligned image should be on the bottom of the sprite) or vertical positioning in case of aligning to the right side.<br />
If you have 2 or more images aligned to the bottom right corner, then you will have to make at least 2 sprites (or keep 1 image separated).
</li>
<li>
If you have a margin around your existing background image then you also should leave a margin inside the sprite
</li>
<li>
There are a few tools exists for creating sprites. I like this one: <a href="http://smartsprites.osinski.name/">SmartSprites</a>, because it covers most of those complicated cases, supports PNG8 and it&#8217;s able to convert Stylesheet files. At the moment the tool is in alpha stage but already looks promising.
</li>
<li>Images containing Photos and Avatars can be joined into separated sprite in JPEG format. Size of file with a photo in JPEG format will be smaller than file in PNG format. Just don&#8217;t try to convert every image you have into sprites. If your page contains 1-2 photos keep them as is. But if the main landing page of the site contains a serie of thumbnails/avatars that changed once per 1 day, then probably they are good candidates to be joined into a JPEG sprite. You should specify appropriate <a href="http://www.webscalingblog.com/performance/caching-http-headers-cache-control-max-age.html">Cache-Control: max-age</a> for this sprite.<br />
Keep in mind that if there are pictures that change every few minutes then it would unpractical to include them into sprite because then you would have to refresh the sprite very often.
</li>
<p><H3>CSS Sprite Problems</h3>
<li><strong>SEO, user agents with disabled images</strong><br />
If we use DIV or SPAN elements as image container we lose possibility to define ALT attribute. This means that visitors will not see the content (value of ALT attribute) if they are browsing with turned off images. It takes place even if we transform ALT=&#8221;" into TITLE=&#8221;".<br />
It also affects search engine spiders, they also don&#8217;t see your content.<br />
In case of images inside links it&#8217;s even worse since visitors don&#8217;t see the links and search engines skip your anchor keywords.<br />
Happily, there is an easy solution exists. We should use IMG tag with transparent GIF as image container:<br />
<code>&lt;img src="/images/transparent.gif" class="i_logo" <strong>alt="keywords here"</strong> /&gt;</code>
</li>
<li><strong>Complexity of Building Image Sprites</strong><br />
Applying Image Sprites involves putting effort into building and maintaining Sprites and changing Stylesheet. This can be time consuming especially if you often change images.<br />
In addition, there are cases when you cannot put all images into sprites. I already mentioned case with several background images aligned to bottom right. Another case is when you have element with size depending on its internal content and you don&#8217;t know how big can it be. Then you cannot use a part of sprite as background because there are another images near the part.
</li>
<li><strong>Complex Sprite Composition</strong><br />
If there are many images, usually each image appears on the page after loading. We join them into one sprite and browser will wait till the whole sprite loaded. This will increase time to the moment when most visible images appear on the page.<br />
If we want an image or a group of images to load faster, we can split the sprite into two or more and preload important sprites via Javascript in document&#8217;s head.<br />
Also, if there are several areas with different images we have to decide how many sprites we need and which image should go in which sprite.<br />
As you see, Sprite Composition can be not so trivial task.
</li>
<li><strong>View Image</strong><br />
Visitors cannot view/download separate images inside a sprite. This is another problem if you want some of the images to be shared.
</li>
]]></content:encoded>
			<wfw:commentRss>http://www.webscalingblog.com/performance/using-css-image-sprites.html/feed</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Caching HTTP Headers, Cache-Control: max-age</title>
		<link>http://www.webscalingblog.com/performance/caching-http-headers-cache-control-max-age.html</link>
		<comments>http://www.webscalingblog.com/performance/caching-http-headers-cache-control-max-age.html#comments</comments>
		<pubDate>Fri, 19 Sep 2008 17:57:01 +0000</pubDate>
		<dc:creator>Nail</dc:creator>
				<category><![CDATA[Caching]]></category>
		<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.webscalingblog.com/?p=11</guid>
		<description><![CDATA[Caching speeds up repeated page views and saves a lot of traffic by preventing downloading of unchanged content every page view.
We can use Cache-Control: max-age=&#8230; to inform browser that the component won&#8217;t be changed for defined period. This way we avoid unneeded further requests if browser already has the component in its cache and therefore [...]]]></description>
			<content:encoded><![CDATA[<p>Caching speeds up repeated page views and saves a lot of traffic by preventing downloading of unchanged content every page view.<br />
We can use <strong>Cache-Control: max-age=&#8230;</strong> to inform browser that the component won&#8217;t be changed for defined period. This way we avoid unneeded further requests if browser already has the component in its cache and therefore primed-cache page views will be performed faster.<br />
Modern browsers able to cache static files even without any cache control headers using some heuristic methods but they will do it more efficient if we define caching headers implicitly.<br />
<span id="more-11"></span><br />
For Apache2 you can enable max-age using <a href="http://httpd.apache.org/docs/2.0/mod/mod_expires.html">mod_expires</a>:<code><br />
ExpiresActive On<br />
ExpiresByType image/gif "access plus 1 month"<br />
ExpiresByType image/png "access plus 1 month"<br />
ExpiresByType image/jpeg "access plus 1 month"<br />
ExpiresByType text/css "access plus 1 month"<br />
ExpiresByType text/javascript "access plus 1 month"<br />
ExpiresByType application/x-javascript "access plus 1 month"<br />
ExpiresByType application/x-shockwave-flash "access plus 1 month"<br />
</code></p>
<p>For Lighttpd there is <a href="http://trac.lighttpd.net/trac/wiki/Docs%3AModExpire">mod_expire</a> module. Enable it in server.modules section:<br />
<code>server.modules = (<br />
...<br />
"mod_expire",<br />
...<br />
)<br />
Then add following directives for directories with static files:<br />
$HTTP["url"] =~ "^/images/" {<br />
expire.url = ( "" => "access 30 days" )<br />
}<br />
</code></p>
<p>Max-age for Nginx server can be enabled using <a href="http://sysoev.ru/nginx/docs/http/ngx_http_headers_module.html">ngx_http_headers_module</a>:<br />
<code>expires max;</code></p>
<p>Now web server sends the caching header for static files:<br />
<code>Cache-Control: max-age=2592000</code></p>
<p>In case of design change we should prevent using outdated content that browsers have in their caches. This can be done by adding file versions to filenames:<br />
<code>script.js -> script1.js -> script2.js -> ... etc</code></p>
<p><strong>Cache-control: max-age</strong> can be useful also when we output HTML. Imagine pages generated by PHP that changed not so often, once per day or even longer. But browsers still have to download HTML every page view.<br />
We can improve it by sending max-age value in PHP.<br />
<code>header('Cache-Control: max-age=28800');</code></p>
<p>This way we set desirable cache lifetime to 8 hours. Now if someone is clicking a link for second time within 8 hours period he gets the page instantly.</p>
<p>Max-age also helps to make proxy servers more efficient. We can easily organize transparent server-side caching by adding proxy server to web frontend.</p>
<p>Note that there is not easy case if pages have content that changes often and that&#8217;s relevant.<br />
For example, there can be difficulties in caching pages with login form that transforms into some box with «Hello username» after user login or if there are user comments, the user who posted commentary will not see it. Because we cannot ask browser to destroy cache entry, it will still get the old page from cache.<br />
The solution can be using Javascript to generate login box (requires enabled Javascript). If we set a cookie after user logged in, we can check it on client-side and generate suitable content for the logged in user. This way the content will be the same from server side view and can be cached.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.webscalingblog.com/performance/caching-http-headers-cache-control-max-age.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>GZIP Compression and Javascript Minification</title>
		<link>http://www.webscalingblog.com/performance/gzip-compression-and-javascript-minification.html</link>
		<comments>http://www.webscalingblog.com/performance/gzip-compression-and-javascript-minification.html#comments</comments>
		<pubDate>Fri, 19 Sep 2008 06:25:38 +0000</pubDate>
		<dc:creator>Nail</dc:creator>
				<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.webscalingblog.com/?p=8</guid>
		<description><![CDATA[Compressing content on server before sending it over HTTP is a good practice since it saves a lot of traffic and makes transfers faster.
For example, Prototype.js is 123KB size and after Gzip compression it becomes only 28KB.
GZIP Compression also affects HTML so even primed cache requests that downloads mostly only HTML will be faster.

So we [...]]]></description>
			<content:encoded><![CDATA[<p>Compressing content on server before sending it over HTTP is a good practice since it saves a lot of traffic and makes transfers faster.<br />
For example, Prototype.js is 123KB size and after Gzip compression it becomes only 28KB.<br />
GZIP Compression also affects HTML so even primed cache requests that downloads mostly only HTML will be faster.<br />
<span id="more-8"></span><br />
So we should enable server-side compression of textual components: Stylesheets, Javascript, HTML, XML (there is no need to compress images as they already compressed).</p>
<p>For Apache2:<br />
compression done by <a href="http://httpd.apache.org/docs/2.0/mod/mod_deflate.html">mod_deflate</a><br />
<code>AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/x-javascript text/xml<br />
</code></p>
<p>For Lighttpd:<br />
enable <a href="http://trac.lighttpd.net/trac/wiki/Docs%3AModCompress">mod_compress</a> in lighttpd.conf and add configuration directives:<br />
<code>compress.cache-dir       = "/tmp/"<br />
compress.filetype          = ("text/plain", "text/html", "application/x-javascript", "text/css", "text/javascript", "text/xml")<br />
</code></p>
<p>Nginx configuration directives:<br />
<code>gzip             on;<br />
gzip_min_length  1000;<br />
gzip_types       text/html text/plain text/css text/javascript application/x-javascript text/xml;</code><br />
(See <a href="http://sysoev.ru/nginx/docs/http/ngx_http_gzip_module.html">ngx_gzip_module documentation</a> for details)</p>
<p>Besides compression there is minification (removing comments and unnecessary whitespaces) that can further reduce size of Javascript.<br />
Minified Prototype.js becomes 92KB size uncompressed and 23KB compressed, this is 20% less than compressed non-minified script.<br />
There are number of tools for minification exists, I&#8217;m using JSMin (<a href="http://crockford.com/javascript/jsmin">http://crockford.com/javascript/jsmin</a>).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.webscalingblog.com/performance/gzip-compression-and-javascript-minification.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
