{"id":828,"date":"2015-02-24T14:41:16","date_gmt":"2015-02-24T14:41:16","guid":{"rendered":"https:\/\/gosqeng.test\/?p=828"},"modified":"2019-11-28T11:54:25","modified_gmt":"2019-11-28T11:54:25","slug":"optimise-node-http-server","status":"publish","type":"post","link":"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server","title":{"rendered":"How to save precious bytes on a Node.js server"},"content":{"rendered":"<p>Every day GoSquared&#8217;s tracking infrastructure processes billions of HTTP requests. We&#8217;ve spent a lot of time making the servers that process these requests as efficient as possible. We try\u00a0to reduce the average payload size of those requests as much as possible to keep bandwidth low.<\/p>\n<h2>A typical tracking request<\/h2>\n<p>The average response in one of our tracking requests looks something like this:<\/p>\n<pre><code class=\"lang-javascript\">_gs('_4');<\/code><\/pre>\n<p>That&#8217;s ten bytes. Now, you might think that&#8217;s pretty damn small but there&#8217;s a problem. Here are the actual bytes that get sent over the wire from one of our servers to serve that request:<\/p>\n<pre><code class=\"lang-http\">HTTP\/1.1 200 OK\\r\\n\nConnection: keep-alive\\r\\n\nTransfer-Encoding: chunked\\r\\n\nContent-Type: text\/javascript\\r\\n\nDate: Wed, 18 Feb 2015 17:44:12 GMT\\r\\n\n\\r\\n\n10\\r\\n\n_gs('_4');\\r\\n\n0\\r\\n\n\\r\\n<\/code><\/pre>\n<p>Ouch! That&#8217;s quite a lot of extra stuff added to the\u00a0actual message body. Those headers add up. If you&#8217;re\u00a0unfamiliar with the behaviour of the Transfer-Encoding header, you may also be wondering what&#8217;s up with that <code>10\\r\\n...\\r\\n0\\r\\n\\r\\n<\/code>\u00a0wrapped around our response payload. It&#8217;s\u00a0called <strong>chunked<\/strong> encoding and is a way of sending data over HTTP when you don&#8217;t know in advance exactly how big the payload will be.<\/p>\n<h2>Content-Length vs. Transfer-Encoding: chunked<\/h2>\n<p>When a browser (or any other client making a HTTP request) processes the response from a server, it needs to know when it&#8217;s finished. Essentially, the client needs to be able to tell the difference between the server having finished sending its response data versus\u00a0there being a brief pause in bytes coming in over the wire. There are two ways this can happen:<\/p>\n<h3>The Content-Length header<\/h3>\n<p>This is the simplest way for a server to tell the browser when it&#8217;s done sending data. When the response is sent, the header section will include a line of the format <code>Content-Length: XYZ<\/code>. The browser then knows that after the header section, there are <code>XYZ<\/code> bytes of body to read, after which the response is complete.<\/p>\n<p>However, there&#8217;s a problem with this. The server might want to start sending the response before knowing how big it&#8217;s going to be. For example, if it&#8217;s reading a large file from disk, or proxying a request from elsewhere, it doesn&#8217;t want to have to load the whole response into memory in order to read its byte length and <em>then<\/em> send it out over the wire. The solution to this problem is <strong>chunked encoding<\/strong>.<\/p>\n<h3>Transfer-Encoding: chunked<\/h3>\n<p>Chunked encoding is signified by the presence of a <code>Transfer-Encoding: chunked<\/code> header in the response. The server then sends the response in &#8220;chunks&#8221; which take the format <code>LENGTH\\r\\nPAYLOAD\\r\\n. <\/code>Finally, it then finished the response with a zero-length chunk, as can be seen above. This is great for servers that don&#8217;t need to know payload size ahead-of-time. It&#8217;s\u00a0not-so-great for situations like ours. We send billions of teeny-tiny responses every day and we&#8217;re counting every single byte.<\/p>\n<h2>What we did<\/h2>\n<p>Prior to a few days ago, all responses sent from our tracking endpoints used chunked encoding \u00a0because that&#8217;s the default behaviour for a HTTP server created in Node.js. By explicitly setting a <code>Content-Length<\/code> header, we effectively disabled chunked encoding and saved a bunch of bytes in the process.<\/p>\n<p>Here&#8217;s the line that we added:<\/p>\n<pre><code class=\"lang-javascript\">response.setHeader('Content-Length', Buffer.byteLength(body));<\/code><\/pre>\n<p>This changed the response payload from what you saw\u00a0above, to this:<\/p>\n<pre><code class=\"lang-http\">HTTP\/1.1 200 OK\\r\\n\nConnection: keep-alive\\r\\n\nContent-Length: 9\\r\\n\nContent-Type: text\/javascript\\r\\n\nDate: Wed, 18 Feb 2015 17:44:12 GMT\\r\\n\n\\r\\n\n_gs('_4')<\/code><\/pre>\n<p>Wahey, that saved us a whole pile of bytes. You&#8217;ll notice we also removed the trailing semicolon from the payload. This actually saves us <em>2\u00a0bytes<\/em> because it reduced the content length from 10 to 9. I call that a win!<\/p>\n<p>All in all, this line of code reduced our outbound data transfer by 21 bytes per request. That may not seem like much, but it&#8217;s more than 10% when you&#8217;re saving 21 bytes on a 160-byte payload.<\/p>\n<p>So there you have it. We saved more than 10% on our data throughput by adding one line of code. I think it&#8217;s safe to say we&#8217;re a bit of an edge-case as far as this optimisation is concerned &#8211; not many other people will be sending billions of teeny-tiny responses where a difference of one or two bytes can make a high percentage difference. But still, not bad.<\/p>\n<p><em><a href=\"http:\/\/gosquared.us2.list-manage.com\/subscribe?u=aff6f5e0c81c6ff5bee968351&amp;id=2eefc61326\" target=\"_blank\" rel=\"noopener noreferrer\">If you like reading about Node.js dev ops, please join our engineering mailing list. Expect occasional engineering posts and news for developers using GoSquared.<\/a><\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Every day GoSquared&#8217;s tracking infrastructure processes billions of HTTP requests. We&#8217;ve spent a lot of time making the servers that&#8230;<\/p>\n","protected":false},"author":5,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1452],"tags":[],"class_list":["post-828","post","type-post","status-publish","format-standard","hentry","category-engineering"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v18.6 (Yoast SEO v19.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>How to save precious bytes on a Node.js server - GoSquared Engineering<\/title>\n<meta name=\"description\" content=\"Every day GoSquared&#039;s tracking infrastructure processes billions of HTTP requests. We want to make the servers that process these requests as efficient as possible.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to save precious bytes on a Node.js server\" \/>\n<meta property=\"og:description\" content=\"Every day GoSquared&#039;s tracking infrastructure processes billions of HTTP requests. We want to make the servers that process these requests as efficient as possible.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server\" \/>\n<meta property=\"og:site_name\" content=\"GoSquared Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/GoSquared\" \/>\n<meta property=\"article:published_time\" content=\"2015-02-24T14:41:16+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-11-28T11:54:25+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@floopily\" \/>\n<meta name=\"twitter:site\" content=\"@GoSquared\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"JT\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/#organization\",\"name\":\"GoSquared\",\"url\":\"https:\/\/www.gosquared.com\/blog\/\",\"sameAs\":[\"https:\/\/instagram.com\/gosquaredteam\",\"https:\/\/www.linkedin.com\/company\/go-squared-ltd.\",\"https:\/\/www.facebook.com\/GoSquared\",\"https:\/\/twitter.com\/GoSquared\"],\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.gosquared.com\/blog\/wp-content\/uploads\/2015\/07\/gosquared.png\",\"contentUrl\":\"https:\/\/www.gosquared.com\/blog\/wp-content\/uploads\/2015\/07\/gosquared.png\",\"width\":1270,\"height\":250,\"caption\":\"GoSquared\"},\"image\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/#website\",\"url\":\"https:\/\/www.gosquared.com\/blog\/\",\"name\":\"GoSquared Blog\",\"description\":\"Turn visitors into customers.\",\"publisher\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.gosquared.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#webpage\",\"url\":\"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server\",\"name\":\"How to save precious bytes on a Node.js server - GoSquared Engineering\",\"isPartOf\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/#website\"},\"datePublished\":\"2015-02-24T14:41:16+00:00\",\"dateModified\":\"2019-11-28T11:54:25+00:00\",\"description\":\"Every day GoSquared's tracking infrastructure processes billions of HTTP requests. We want to make the servers that process these requests as efficient as possible.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.gosquared.com\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to save precious bytes on a Node.js server\"}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#webpage\"},\"author\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/bfcd35bf2eba92ecbeea67937cd23eef\"},\"headline\":\"How to save precious bytes on a Node.js server\",\"datePublished\":\"2015-02-24T14:41:16+00:00\",\"dateModified\":\"2019-11-28T11:54:25+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#webpage\"},\"wordCount\":684,\"commentCount\":5,\"publisher\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/#organization\"},\"articleSection\":[\"Engineering\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#respond\"]}]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/bfcd35bf2eba92ecbeea67937cd23eef\",\"name\":\"JT\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/772e026206b900a5ba17ebbe63e34a4c8a9103524cf0ba3accfa38b14d7d03ba?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/772e026206b900a5ba17ebbe63e34a4c8a9103524cf0ba3accfa38b14d7d03ba?s=96&d=mm&r=g\",\"caption\":\"JT\"},\"description\":\"JT is a co-founder and the lead front-end engineer at GoSquared. He's responsible for the shiniest of the shiny projects we work on.\",\"sameAs\":[\"https:\/\/twitter.com\/floopily\"],\"url\":\"https:\/\/www.gosquared.com\/blog\/author\/jt\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"How to save precious bytes on a Node.js server - GoSquared Engineering","description":"Every day GoSquared's tracking infrastructure processes billions of HTTP requests. We want to make the servers that process these requests as efficient as possible.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server","og_locale":"en_US","og_type":"article","og_title":"How to save precious bytes on a Node.js server","og_description":"Every day GoSquared's tracking infrastructure processes billions of HTTP requests. We want to make the servers that process these requests as efficient as possible.","og_url":"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server","og_site_name":"GoSquared Blog","article_publisher":"https:\/\/www.facebook.com\/GoSquared","article_published_time":"2015-02-24T14:41:16+00:00","article_modified_time":"2019-11-28T11:54:25+00:00","twitter_card":"summary_large_image","twitter_creator":"@floopily","twitter_site":"@GoSquared","twitter_misc":{"Written by":"JT","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Organization","@id":"https:\/\/www.gosquared.com\/blog\/#organization","name":"GoSquared","url":"https:\/\/www.gosquared.com\/blog\/","sameAs":["https:\/\/instagram.com\/gosquaredteam","https:\/\/www.linkedin.com\/company\/go-squared-ltd.","https:\/\/www.facebook.com\/GoSquared","https:\/\/twitter.com\/GoSquared"],"logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.gosquared.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.gosquared.com\/blog\/wp-content\/uploads\/2015\/07\/gosquared.png","contentUrl":"https:\/\/www.gosquared.com\/blog\/wp-content\/uploads\/2015\/07\/gosquared.png","width":1270,"height":250,"caption":"GoSquared"},"image":{"@id":"https:\/\/www.gosquared.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"WebSite","@id":"https:\/\/www.gosquared.com\/blog\/#website","url":"https:\/\/www.gosquared.com\/blog\/","name":"GoSquared Blog","description":"Turn visitors into customers.","publisher":{"@id":"https:\/\/www.gosquared.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.gosquared.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#webpage","url":"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server","name":"How to save precious bytes on a Node.js server - GoSquared Engineering","isPartOf":{"@id":"https:\/\/www.gosquared.com\/blog\/#website"},"datePublished":"2015-02-24T14:41:16+00:00","dateModified":"2019-11-28T11:54:25+00:00","description":"Every day GoSquared's tracking infrastructure processes billions of HTTP requests. We want to make the servers that process these requests as efficient as possible.","breadcrumb":{"@id":"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.gosquared.com\/blog\/optimise-node-http-server"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.gosquared.com\/blog"},{"@type":"ListItem","position":2,"name":"How to save precious bytes on a Node.js server"}]},{"@type":"Article","@id":"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#article","isPartOf":{"@id":"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#webpage"},"author":{"@id":"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/bfcd35bf2eba92ecbeea67937cd23eef"},"headline":"How to save precious bytes on a Node.js server","datePublished":"2015-02-24T14:41:16+00:00","dateModified":"2019-11-28T11:54:25+00:00","mainEntityOfPage":{"@id":"https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#webpage"},"wordCount":684,"commentCount":5,"publisher":{"@id":"https:\/\/www.gosquared.com\/blog\/#organization"},"articleSection":["Engineering"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.gosquared.com\/blog\/optimise-node-http-server#respond"]}]},{"@type":"Person","@id":"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/bfcd35bf2eba92ecbeea67937cd23eef","name":"JT","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/772e026206b900a5ba17ebbe63e34a4c8a9103524cf0ba3accfa38b14d7d03ba?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/772e026206b900a5ba17ebbe63e34a4c8a9103524cf0ba3accfa38b14d7d03ba?s=96&d=mm&r=g","caption":"JT"},"description":"JT is a co-founder and the lead front-end engineer at GoSquared. He's responsible for the shiniest of the shiny projects we work on.","sameAs":["https:\/\/twitter.com\/floopily"],"url":"https:\/\/www.gosquared.com\/blog\/author\/jt"}]}},"wps_subtitle":"Micro-optimisations can sometimes make a (relatively) big difference","_links":{"self":[{"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/posts\/828","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/comments?post=828"}],"version-history":[{"count":0,"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/posts\/828\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/media?parent=828"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/categories?post=828"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/tags?post=828"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}