Authorization

Browsers cannot start rendering till all Stylesheets loaded. Visitors see blank page (or previous page) during loading of CSS so it’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’s look what can be done to reduce time to render.
1. First, we want to move CSS components to the HEAD section of HTML document so the Stylesheet will be loaded with first priority.
Worse case is when CSS included on the bottom – 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.
2. If we have several Stylesheets we join them into one bigger CSS file. As described in the previous post about Image Sprites, 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’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.
3. 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.
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.
More detailed:
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’t inline Stylesheet component if visitor comes with primed cache, use an external definition instead.
Postload the external Stylesheet component when visitor comes first time (without the cookie). See example below.
Cookie named “jsl” will be set when user sees this page for first time and next requests will use external Stylesheet component.
You can set such cookie in PHP:
$is_first = !isset($_COOKIE['jsl']);
if ($is_first)
setcookie('jsl',1,time()+3600*24,'/','.site.com',false,true);
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.
It will work correctly even in case user comes with empty cache (for example if our cache got forced out because cache size limit).
Style.css – global CSS file. Probably in real application instead of readfile(’style.css’) you will include only part of the Stylesheet that’s needed to render current page.
To find out CSS that’s really required for current page, you can use a Firefox extension: Dust-Me Selectors (note that this tool may not count CSS rules for elements you assign dynamically in Javascript).
Replace existing opening tag
with
>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’t changed.
There are two validation methods: one is based on Last-Modified and the other is based on Etag.
Last-Modified
Server sends Last-Modified header with datetime value that means the time when content was changed last time.
Cache-Control: must-revalidate
Last-Modified: 15 Sep 2008 17:43:00 GMT
The first header Cache-Control: must-revalidate means that browser must send validation request every time even if there is already cache entry exists for this object.
Browser receives the content and stores it in the cache along with the last modified value.
Next time browser will send additional header:
If-Modified-Since: 15 Sep 2008 17:43:00 GMT
This header means that browser has cache entry that was last changed 17:43.
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.
If there were no changes since the previous request then there will be short empty-body answer:
HTTP/1.x 304 Not Modified
And browser will use the cached content.
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, it provides a tar.gz file with HTML file and CSS files with encoded images in textual format.
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:
The advantages are:
This is a new tool that’s in beta stage so I would not use it in production.
The tool written in Java and the authors are going to opensource it as soon as release-candidate is ready.
(via Habr article in Russian)
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 SmartSprites.
The advantage of Image Sprites over Data:URI Sprites is that it’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.
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 – 4-6 connections, Google Chrome – 6 connections.
Let’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 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.
The popular solution is to use CSS Image Sprites:
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 background-position.
This approach is compatible with modern browsers (and even with IE4).
Let’s review the process step by step:
Step1.
Replace image entries with
Caching speeds up repeated page views and saves a lot of traffic by preventing downloading of unc
hanged content every page view.
We can use Cache-Control: max-age=… to inform browser that the component won’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.
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.
For Apache2 you can enable max-age using mod_expires:
ExpiresActive On
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType text/css "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
ExpiresByType application/x-shockwave-flash "access plus 1 month"
For Lighttpd there is mod_expire module. Enable it in server.modules section:
server.modules = (
...
"mod_expire",
...
)
Then add following directives for directories with static files:
$HTTP["url"] =~ "^/images/" {
expire.url = ( "" => "access 30 days" )
}
Max-age for Nginx server can be enabled using ngx_http_headers_module:
expires max;
Now web server sends the caching header for static files:
Cache-Control: max-age=2592000
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:
script.js -> script1.js -> script2.js -> ... etc
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 should enable server-side compression of textual components: Stylesheets, Javascript, HTML, XML (there is no need to compress images as they already compressed).
For Apache2:
compression done by mod_deflate
AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/x-javascript text/xml
For Lighttpd:
enable mod_compress in lighttpd.conf and add configuration directives:
compress.cache-dir = "/tmp/"
compress.filetype = ("text/plain", "text/html", "application/x-javascript", "text/css", "text/javascript", "text/xml")
Nginx configuration directives:
gzip on;
gzip_min_length 1000;
gzip_types text/html text/plain text/css text/javascript application/x-javascript text/xml;Besides compression there is minification (removing comments and unnecessary whitespaces) that can further reduce size of Javascript.
Minified Prototype.js becomes 92KB size uncompressed and 23KB compressed, this is 20% less than compressed non-minified script.
There are number of tools for minification exists, I’m using JSMin (http://crockford.com/javascript/jsmin).
Original ActiveRecord sessions st ore is slow. It is fine for some low traffic sites, but it is too slow to use it for something larger. First of all, it is slow because ActiveRecord itself is slow. It is powerful ORM framework, but it is obviously overkill for such a simple task as a sessions management.
There are other possible solutions like cookie session store (session size is limited, no sensible data in sessions), memcached (no persistence + harder to implement high-available solutions).
That is why SqlSession store was created. It works with mysql directly with database APIs and works much faster than original AR session store. But sometimes it is still slow too because:
As a solution for mentioned problems FastSessions Rails plugin was born.
So, first of all, we removed id field from the sessions table, so we do not need to use auto-increment locks. Next step was to make lookups faster and we’ve used a following technique: instead of using (session_id) as a lookup key, we’ve started using (CRC32(session_id), session_id) – two-columns key which really helps MySQL to find sessions faster because key cardinality is higher (so, mysql is able to find a record earlier w/o checking a lots of index rows). We’ve benchmarked this approach and it shows 10-15% performance gain on large sessions tables.