0
GETTING AN “A” IN YSLOW        Web Performance Optimization 101     Nick Zadrozny <nick@beyondthepath.com>
HI, MY NAME IS NICK.     I MAKE WEBSITES.
I’M EMBARRASSED  MY WEBSITES ARE SLOW
<METAPHOR>
You’re driving a brand new carIt’s moving really slowlyYou can smell smokePeople are giving you looks
You’re driving a brand new carIt’s moving really slowly     Return the carYou can smell smoke           Rebuild the carPeo...
D’oh!
</METAPHOR>
THE POINTLOW-HANGING FRUIT…
YAHOO!THEY MAKE WEBSITES
Yahoo!          34 “Best Practices” for Exceptional Performance1. Minimize HTTP            9. Reduce DNS Lookups        19...
YSLOWPLUGIN FOR FIREBUG
YSlow                 Tests Your Site on 13 RulesMinimize HTTP          Put Stylesheets at      Reduce DNSRequests        ...
MINIMIZE HTTP REQUESTS
Minimize HTTP Requests HTTP Requests are Expensive! Net work latency + server latency + net work latency + download time. ...
MAKE JAVASCRIPTAND CSS EXTERNAL
Make Javascript             and CSS ExternalEasier to maintain, less HTML to sendBrowser caching prevents later requestsHe...
MINIFY JAVASCRIPT     AND CSS
Minify Javascript and CSS≈20% size reduction         html,body,div,span,applet,object,iframe,h1,h2                        ...
STYLESHEETS AT THE TOPSCRIPTS AT THE BOTTOM
Stylesheets at the top          Scripts at the bottom                               <!DOCTYPE html>Javascript shouldn’t bl...
GZIP COMPONENTS
GZip ComponentsAnother nice size reductionMost devices support itCSS, Javascript, text/html,text/plain
SOME REALLY EASY ONES…
AVOID CSS EXPRESSIONS
AVOID REDIRECTS
REMOVE DUPLICATE    SCRIPTS
REDUCE DNS LOOKUPS
Apache gives this for free for most of yourassets.Rails gives this for free for all of theresponses it sends back. It also...
Configure ETagsWeb servers likely give you a lot for freeRails gives you some ETagging for freeRails ETag and Last-Modifie...
ADD AN EXPIRES ORCACHE-CONTROL HEADER
Add an Expires HeaderCheck out mod_expires for ApacheRails already does “soft” cache bustingHard cache busting (for CDNs)…...
USE A CDN
#!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3...
#!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3...
#!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3...
#!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3...
#!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3...
#!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3...
#!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3...
#!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3...
#!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3...
THAT’S IT!
ARTICLE FORTHCOMING  BEYOND THE PATH.COM
Earning an "A" in YSlow
Upcoming SlideShare
Loading in...5
×

Earning an "A" in YSlow

787

Published on

In which we examine common website performance optimization techniques, and how you can implement them in your Ruby on Rails application.

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
787
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • Transcript of "Earning an "A" in YSlow"

    1. 1. GETTING AN “A” IN YSLOW Web Performance Optimization 101 Nick Zadrozny <nick@beyondthepath.com>
    2. 2. HI, MY NAME IS NICK. I MAKE WEBSITES.
    3. 3. I’M EMBARRASSED MY WEBSITES ARE SLOW
    4. 4. <METAPHOR>
    5. 5. You’re driving a brand new carIt’s moving really slowlyYou can smell smokePeople are giving you looks
    6. 6. You’re driving a brand new carIt’s moving really slowly Return the carYou can smell smoke Rebuild the carPeople are giving you looks Disengage the hand brake
    7. 7. D’oh!
    8. 8. </METAPHOR>
    9. 9. THE POINTLOW-HANGING FRUIT…
    10. 10. YAHOO!THEY MAKE WEBSITES
    11. 11. Yahoo! 34 “Best Practices” for Exceptional Performance1. Minimize HTTP 9. Reduce DNS Lookups 19. Reduce the Number of 27. Choose <link> over Requests DOM Elements @import 10. Minify JavaScript and2. Use a Content CSS 20. Split Components 28. Avoid Filters Delivery Network Across Domains 11. Avoid Redirects 29. Optimize Images3. Add an Expires or a 21. Minimize the Number Cache-Control 12. Remove Duplicate of iframes 30. Optimize CSS Sprites Header Scripts 22. No 404s 31. Don’t Scale Images in4. Gzip Components 13. Configure ETags HTML 23. Reduce Cookie Size5. Put Stylesheets at the 14. Make Ajax Cacheable 32. Make favicon.ico Top 24. Use Cookie-free Small and Cacheable 15. Flush the Buffer Early Domains for6. Put Scripts at the Components 33. Keep Components 16. Use GET for AJAX under 25K (mobile) Bottom Requests 25. Minimize DOM7. Avoid CSS Access 34. Pack Components 17. Post-load into a Multipart Expressions Components 26. Develop Smart Event Document (mobile)8. Make Javascript and 18. Preload Components Handlers CSS External
    12. 12. YSLOWPLUGIN FOR FIREBUG
    13. 13. YSlow Tests Your Site on 13 RulesMinimize HTTP Put Stylesheets at Reduce DNSRequests the Top LookupsUse a Content Put Scripts at the Minify JavaScriptDelivery Network Bottom and CSSAdd an Expires Avoid CSS Avoid Redirectsor a Cache- ExpressionsControl Header Remove Make Javascript Duplicate ScriptsGzip and CSS ExternalComponents Configure ETags
    14. 14. MINIMIZE HTTP REQUESTS
    15. 15. Minimize HTTP Requests HTTP Requests are Expensive! Net work latency + server latency + net work latency + download time. This rule is fundamental, the Catch-All. Everything else is based on it. Asset packaging reduces overall number of requests. Compression & minification reduce the size of responses. Reordering includes lets important stuff go first. ETags and Last-Modified checks reduce the content of an HTTP Request. Browser caching completely eliminates subsequent requests. Major win.
    16. 16. MAKE JAVASCRIPTAND CSS EXTERNAL
    17. 17. Make Javascript and CSS ExternalEasier to maintain, less HTML to sendBrowser caching prevents later requestsHelp order requests by priorityRails makes this pretty easy,stylesheet_link_tagjavascript_include_tag
    18. 18. MINIFY JAVASCRIPT AND CSS
    19. 19. Minify Javascript and CSS≈20% size reduction html,body,div,span,applet,object,iframe,h1,h2 ,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym, address,big,cite,code,del,dfn,em,font,img,insUsing a framework? You’re ,kbd,q,s,samp,small,strike,strong,sub,sup,tt, var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,f orm,label,legend,table,caption,tbody,tfoot,thprobably halfway there. ead,tr,th,td{margin:0;padding:0;border: 0;outline:0;font-size:100%;vertical- align:baseline;background:transparent} body{line-height:1}ol,ul{list-style:none}YUI Compressor + Rake blockquote,q{quotes:none}blockquote:before,bl ockquote:after,q:before,q:after{content:;co ntent:none}:focus{outline:0}ins{text- decoration:none}del{text-decoration:line- through}table{border-collapse:collapse; border-spacing:0}
    20. 20. STYLESHEETS AT THE TOPSCRIPTS AT THE BOTTOM
    21. 21. Stylesheets at the top Scripts at the bottom <!DOCTYPE html>Javascript shouldn’t block <html> <head>more important rendering <title>Your Awesome Site</title> <%= stylesheet_link_tag application %> <%= yield(:head) %> </head> <body>Pass blocks to the head and <div id="page"> <header>foot from within your views: <h1>Awesome Site</h1> </header> <article> <%= yield %><% content_for(:foot) do %> </article> <%= javascript_tag "…" %> </div> <%= javascript_include_tag application %><% end %> <%= yield(:foot) %> </body> </html>
    22. 22. GZIP COMPONENTS
    23. 23. GZip ComponentsAnother nice size reductionMost devices support itCSS, Javascript, text/html,text/plain
    24. 24. SOME REALLY EASY ONES…
    25. 25. AVOID CSS EXPRESSIONS
    26. 26. AVOID REDIRECTS
    27. 27. REMOVE DUPLICATE SCRIPTS
    28. 28. REDUCE DNS LOOKUPS
    29. 29. Apache gives this for free for most of yourassets.Rails gives this for free for all of theresponses it sends back. It also has somehelpers that will let you potentially skip dbcalls and expensive view rendering. CONFIGURE ETAGS
    30. 30. Configure ETagsWeb servers likely give you a lot for freeRails gives you some ETagging for freeRails ETag and Last-Modified helperdef show @article = Article.find(params[:id]) if stale?(:etag => @article, :last_modified => @article.created_at.utc) @statistics = @article.really_expensive_call respond_to do |format| # etc end endend
    31. 31. ADD AN EXPIRES ORCACHE-CONTROL HEADER
    32. 32. Add an Expires HeaderCheck out mod_expires for ApacheRails already does “soft” cache bustingHard cache busting (for CDNs)…config.action_controller.asset_host ="http://assets.example.com/#{REVISION}"
    33. 33. USE A CDN
    34. 34. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
    35. 35. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
    36. 36. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
    37. 37. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
    38. 38. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
    39. 39. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
    40. 40. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
    41. 41. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: set the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
    42. 42. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
    43. 43. THAT’S IT!
    44. 44. ARTICLE FORTHCOMING BEYOND THE PATH.COM
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×