{"id":146,"date":"2026-07-23T14:00:00","date_gmt":"2026-07-23T08:30:00","guid":{"rendered":"https:\/\/softcrony.com\/blog\/?p=146"},"modified":"2026-07-23T14:00:00","modified_gmt":"2026-07-23T08:30:00","slug":"web-performance-checklist-india-2026","status":"publish","type":"post","link":"https:\/\/softcrony.com\/blog\/web-performance-checklist-india-2026\/","title":{"rendered":"The Web Performance Checklist Every Indian Developer Should Bookmark in 2026"},"content":{"rendered":"<p>Web performance in India is a different problem than web performance in the US or Europe. When you&#8217;re optimizing for a user on a 4G connection in Bhopal on a mid-range Android phone, the numbers that matter and the fixes that help are not the same as optimizing for someone on fiber in San Francisco.<\/p>\n<p>This checklist is built specifically for Indian developers building for Indian users.<\/p>\n<h2>Why Indian Web Performance Is Different<\/h2>\n<p>Three factors make India a uniquely challenging performance environment:<\/p>\n<p><strong>Network variability.<\/strong> Indian mobile networks are fast when they&#8217;re good (5G in metro areas can be excellent) and extremely slow when they&#8217;re not (rural 2G\/3G, peak hour congestion, elevator \/ basement dead zones). Your site needs to work on both \u2014 not just the good case.<\/p>\n<p><strong>Device diversity.<\/strong> The Indian smartphone market spans from flagship iPhones in corporate offices to \u20b98,000 Android phones in tier-3 cities. JavaScript-heavy sites that perform beautifully on a high-end device can be completely unusable on low-end hardware.<\/p>\n<p><strong>High latency to Western servers.<\/strong> If your server is in the US or Europe, Indian users are dealing with 150\u2013400ms base latency before your application even starts. This compounds every other performance problem.<\/p>\n<h2>The Checklist<\/h2>\n<h3>Section 1 \u2014 Measure First<\/h3>\n<p>Never optimize what you haven&#8217;t measured. Start here:<\/p>\n<table>\n<thead>\n<tr>\n<th>Tool<\/th>\n<th>What It Measures<\/th>\n<th>India-Specific Tip<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>PageSpeed Insights<\/td>\n<td>Core Web Vitals, opportunities<\/td>\n<td>Check mobile score, not just desktop<\/td>\n<\/tr>\n<tr>\n<td>WebPageTest<\/td>\n<td>Waterfall, real device testing<\/td>\n<td>Test from Mumbai location, on mobile<\/td>\n<\/tr>\n<tr>\n<td>GTmetrix<\/td>\n<td>Waterfall, filmstrip<\/td>\n<td>Set test location to Mumbai<\/td>\n<\/tr>\n<tr>\n<td>Chrome DevTools<\/td>\n<td>Network throttling simulation<\/td>\n<td>Test on &#8220;Slow 4G&#8221; and &#8220;3G&#8221; presets<\/td>\n<\/tr>\n<tr>\n<td>Lighthouse<\/td>\n<td>Full audit with recommendations<\/td>\n<td>Run in incognito, check mobile<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>India baseline targets:<\/strong><\/p>\n<ul>\n<li>LCP (Largest Contentful Paint): under 2.5 seconds on mobile<\/li>\n<li>FID \/ INP (Interaction to Next Paint): under 200ms<\/li>\n<li>CLS (Cumulative Layout Shift): under 0.1<\/li>\n<li>Time to First Byte (TTFB): under 800ms<\/li>\n<li>Total page weight: under 1MB for initial load<\/li>\n<\/ul>\n<h3>Section 2 \u2014 Server and Hosting<\/h3>\n<p>\u2610 <strong>Use a server or CDN with an India edge node<\/strong><\/p>\n<p>Cloudflare (free), AWS Mumbai (ap-south-1), DigitalOcean Bangalore, or Vercel \u2014 all have India-region infrastructure. Never serve Indian users from US-East if you can help it.<\/p>\n<p>\u2610 <strong>Enable HTTP\/2 or HTTP\/3<\/strong><\/p>\n<p>HTTP\/2 multiplexes multiple requests over one connection \u2014 critical for performance on high-latency connections. Check in Chrome DevTools \u2192 Network \u2192 Protocol column. Should show &#8220;h2&#8221; or &#8220;h3&#8221;.<\/p>\n<p>\u2610 <strong>Set proper cache headers on static assets<\/strong><\/p>\n<pre><code># Nginx \u2014 cache static assets for 1 year\r\nlocation ~* \\.(js|css|png|jpg|webp|woff2|ico)$ {\r\n    expires 1y;\r\n    add_header Cache-Control \"public, immutable\";\r\n}<\/code><\/pre>\n<p>\u2610 <strong>Enable Gzip\/Brotli compression<\/strong><\/p>\n<pre><code># Nginx \u2014 enable Brotli (better than Gzip)\r\nbrotli on;\r\nbrotli_comp_level 6;\r\nbrotli_types text\/plain text\/css application\/javascript application\/json image\/svg+xml;<\/code><\/pre>\n<p>\u2610 <strong>Implement server-side caching<\/strong><\/p>\n<p>For Laravel: Redis page caching via Laravel Cache or Nginx FastCGI cache. A page that takes 800ms to generate from the database should serve in 5ms from cache.<\/p>\n<h3>Section 3 \u2014 Images (Biggest Win for Most Indian Sites)<\/h3>\n<p>\u2610 <strong>Convert all images to WebP or AVIF<\/strong><\/p>\n<p>WebP is 30\u201340% smaller than JPEG at the same quality. AVIF is another 20% smaller than WebP. Both are supported on all modern browsers.<\/p>\n<pre><code>\/\/ In HTML \u2014 serve WebP with JPEG fallback\r\n&lt;picture&gt;\r\n  &lt;source srcset=\"hero.avif\" type=\"image\/avif\"&gt;\r\n  &lt;source srcset=\"hero.webp\" type=\"image\/webp\"&gt;\r\n  &lt;img src=\"hero.jpg\" alt=\"Hero image\" loading=\"lazy\"&gt;\r\n&lt;\/picture&gt;<\/code><\/pre>\n<pre><code>\/\/ In Laravel \u2014 auto-convert on upload\r\nuse Intervention\\Image\\Laravel\\Facades\\Image;\r\n\r\n$image = Image::read($request->file('image'));\r\n$image->toWebp(80)->save(storage_path('app\/public\/images\/' . $filename . '.webp'));<\/code><\/pre>\n<p>\u2610 <strong>Serve correctly sized images<\/strong><\/p>\n<p>A 2000px wide image displayed at 400px width wastes 80% of the bandwidth. Use responsive images:<\/p>\n<pre><code>&lt;img\r\n  srcset=\"hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w\"\r\n  sizes=\"(max-width: 600px) 400px, (max-width: 900px) 800px, 1200px\"\r\n  src=\"hero-800.webp\"\r\n  alt=\"Hero\"\r\n  loading=\"lazy\"\r\n&gt;<\/code><\/pre>\n<p>\u2610 <strong>Lazy load all below-the-fold images<\/strong><\/p>\n<pre><code>&lt;img src=\"product.webp\" loading=\"lazy\" alt=\"Product\"&gt;<\/code><\/pre>\n<p>\u2610 <strong>Set explicit width and height on all images<\/strong><\/p>\n<p>This prevents layout shift (CLS) \u2014 one of the most common CWV failures:<\/p>\n<pre><code>&lt;img src=\"product.webp\" width=\"400\" height=\"300\" loading=\"lazy\" alt=\"Product\"&gt;<\/code><\/pre>\n<p>\u2610 <strong>Compress images before upload<\/strong><\/p>\n<p>Target: product images under 100KB, hero images under 200KB, thumbnails under 30KB. Tools: Squoosh (browser), ImageOptim (Mac), Cloudflare Images (automated).<\/p>\n<h3>Section 4 \u2014 JavaScript<\/h3>\n<p>\u2610 <strong>Audit your JavaScript bundle size<\/strong><\/p>\n<pre><code># Vite \u2014 analyze bundle\r\nnpm run build -- --analyze\r\n\r\n# Or install the visualizer plugin\r\nnpm install --save-dev rollup-plugin-visualizer<\/code><\/pre>\n<p>\u2610 <strong>Remove unused JavaScript<\/strong><\/p>\n<p>Every npm package you install adds to your bundle. Audit with:<\/p>\n<pre><code>npx depcheck  # Find unused packages\r\nnpx bundlephobia # Check package sizes before installing<\/code><\/pre>\n<p>\u2610 <strong>Code split at the route level<\/strong><\/p>\n<p>Don&#8217;t load the entire application upfront. Load only the code for the current page:<\/p>\n<pre><code>\/\/ React \u2014 lazy load route components\r\nimport { lazy, Suspense } from 'react';\r\nconst Dashboard = lazy(() => import('.\/pages\/Dashboard'));\r\nconst Analytics = lazy(() => import('.\/pages\/Analytics'));\r\n\r\n\/\/ In router\r\n&lt;Suspense fallback={&lt;Loading \/&gt;}&gt;\r\n  &lt;Dashboard \/&gt;\r\n&lt;\/Suspense&gt;<\/code><\/pre>\n<p>\u2610 <strong>Defer non-critical JavaScript<\/strong><\/p>\n<pre><code>&lt;!-- Load after HTML parsed --&gt;\r\n&lt;script src=\"analytics.js\" defer&gt;&lt;\/script&gt;\r\n\r\n&lt;!-- Load when browser is idle --&gt;\r\n&lt;script&gt;\r\n  window.addEventListener('load', () => {\r\n    import('.\/chat-widget.js');\r\n  });\r\n&lt;\/script&gt;<\/code><\/pre>\n<h3>Section 5 \u2014 CSS<\/h3>\n<p>\u2610 <strong>Remove unused CSS<\/strong><\/p>\n<p>Tailwind CSS already purges unused classes in production. For custom CSS, use PurgeCSS.<\/p>\n<p>\u2610 <strong>Inline critical CSS<\/strong><\/p>\n<p>The CSS needed to render above-the-fold content should be inlined in the HTML head \u2014 not loaded from a separate file:<\/p>\n<pre><code>&lt;!-- Inline critical CSS --&gt;\r\n&lt;style&gt;\r\n  \/* Only the CSS needed for above-the-fold content *\/\r\n  body { margin: 0; font-family: Inter, sans-serif; }\r\n  .hero { ... }\r\n&lt;\/style&gt;\r\n\r\n&lt;!-- Load the rest asynchronously --&gt;\r\n&lt;link rel=\"preload\" href=\"styles.css\" as=\"style\" onload=\"this.rel='stylesheet'\"&gt;<\/code><\/pre>\n<h3>Section 6 \u2014 Fonts<\/h3>\n<p>\u2610 <strong>Use font-display: swap<\/strong><\/p>\n<pre><code>@font-face {\r\n  font-family: 'Inter';\r\n  src: url('\/fonts\/inter.woff2') format('woff2');\r\n  font-display: swap; \/* Show fallback immediately, swap when loaded *\/\r\n}<\/code><\/pre>\n<p>\u2610 <strong>Self-host fonts instead of Google Fonts<\/strong><\/p>\n<p>Google Fonts adds a DNS lookup, a connection, and a download from Google&#8217;s servers. Self-hosting eliminates this. Use <a href=\"https:\/\/gwfh.mranftl.com\/\">Google Webfonts Helper<\/a> to download and self-host any Google Font.<\/p>\n<p>\u2610 <strong>Preconnect to font providers if you must use CDN<\/strong><\/p>\n<pre><code>&lt;link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\"&gt;\r\n&lt;link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin&gt;<\/code><\/pre>\n<p>\u2610 <strong>Only load the weights you use<\/strong><\/p>\n<pre><code>&lt;!-- Don't load all weights --&gt;\r\n&lt;!-- fonts.googleapis.com\/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900 --&gt;\r\n\r\n&lt;!-- Load only what you use --&gt;\r\n&lt;!-- fonts.googleapis.com\/css2?family=Inter:wght@400;600;700 --&gt;<\/code><\/pre>\n<h3>Section 7 \u2014 India-Specific Optimizations<\/h3>\n<p>\u2610 <strong>Design for offline \/ poor connectivity<\/strong><\/p>\n<p>Use Service Workers to cache the application shell. Users on spotty connections should see content, not blank screens or spinners.<\/p>\n<pre><code>\/\/ service-worker.js \u2014 cache app shell\r\nconst CACHE_NAME = 'app-shell-v1';\r\nconst SHELL_URLS = ['\/', '\/css\/app.css', '\/js\/app.js', '\/offline.html'];\r\n\r\nself.addEventListener('install', event => {\r\n  event.waitUntil(\r\n    caches.open(CACHE_NAME).then(cache => cache.addAll(SHELL_URLS))\r\n  );\r\n});<\/code><\/pre>\n<p>\u2610 <strong>Test on real Indian network conditions<\/strong><\/p>\n<p>In Chrome DevTools \u2192 Network \u2192 Throttling, test on &#8220;Slow 4G&#8221; (which represents realistic Indian 4G outside metro areas). If your site is unusable on Slow 4G, a significant chunk of Indian users can&#8217;t use it.<\/p>\n<p>\u2610 <strong>Optimize for Android mid-range devices<\/strong><\/p>\n<p>Test on or emulate a device with 2GB RAM and a mid-range CPU. Heavy JavaScript will cause visible jank on these devices. Use Chrome&#8217;s CPU throttling (6x slowdown) to simulate this in DevTools.<\/p>\n<p>\u2610 <strong>Use Cloudflare in front of your site<\/strong><\/p>\n<p>Cloudflare&#8217;s free plan gives you a global CDN, DDoS protection, and Brotli compression automatically. For Indian users, Cloudflare routes traffic through its Mumbai and Chennai nodes \u2014 reducing latency significantly compared to serving from a single region VPS.<\/p>\n<h3>Section 8 \u2014 Monitoring<\/h3>\n<p>\u2610 <strong>Set up Real User Monitoring (RUM)<\/strong><\/p>\n<p>Synthetic tests (PageSpeed, WebPageTest) show you your site&#8217;s performance in ideal conditions. RUM shows you what real users actually experience. Cloudflare Web Analytics (free), Google Search Console&#8217;s CWV report, or web-vitals.js are all good options.<\/p>\n<pre><code>\/\/ web-vitals.js \u2014 measure real user performance\r\nimport { onCLS, onFID, onLCP, onTTFB, onINP } from 'web-vitals';\r\n\r\nfunction sendToAnalytics(metric) {\r\n  \/\/ Send to your analytics endpoint\r\n  fetch('\/analytics\/vitals', {\r\n    method: 'POST',\r\n    body: JSON.stringify(metric),\r\n  });\r\n}\r\n\r\nonCLS(sendToAnalytics);\r\nonFID(sendToAnalytics);\r\nonLCP(sendToAnalytics);\r\nonTTFB(sendToAnalytics);\r\nonINP(sendToAnalytics);<\/code><\/pre>\n<p>\u2610 <strong>Set up alerts for performance regressions<\/strong><\/p>\n<p>Add a Lighthouse CI check to your GitHub Actions pipeline that fails the build if Core Web Vitals drop below your thresholds:<\/p>\n<pre><code># .github\/workflows\/lighthouse.yml\r\n- name: Run Lighthouse CI\r\n  uses: treosh\/lighthouse-ci-action@v10\r\n  with:\r\n    urls: |\r\n      https:\/\/yoursite.com\r\n    budgetPath: .\/lighthouse-budget.json<\/code><\/pre>\n<h2>Performance Budget for Indian Sites<\/h2>\n<table>\n<thead>\n<tr>\n<th>Resource<\/th>\n<th>Budget<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Total page weight (initial)<\/td>\n<td>&lt; 1MB<\/td>\n<\/tr>\n<tr>\n<td>Images (initial load)<\/td>\n<td>&lt; 500KB<\/td>\n<\/tr>\n<tr>\n<td>JavaScript (initial)<\/td>\n<td>&lt; 200KB gzipped<\/td>\n<\/tr>\n<tr>\n<td>CSS (initial)<\/td>\n<td>&lt; 50KB gzipped<\/td>\n<\/tr>\n<tr>\n<td>Fonts<\/td>\n<td>&lt; 100KB total<\/td>\n<\/tr>\n<tr>\n<td>Third-party scripts<\/td>\n<td>&lt; 100KB<\/td>\n<\/tr>\n<tr>\n<td>Time to Interactive<\/td>\n<td>&lt; 5 seconds (Slow 4G)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If you need a performance audit for your site or want help implementing these optimizations, <a href=\"https:\/\/softcrony.com\/contact\/\">our team at Softcrony has done this for dozens of Indian businesses<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Web performance in India is a different problem than web performance in the US or Europe. When you&#8217;re optimizing for a user on a 4G connection in Bhopal on a mid-range Android phone, the numbers that matter and the fixes that help are not the same as optimizing for someone on fiber in San Francisco. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":148,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[37,51,20,38,126,125],"class_list":["post-146","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-frontend","tag-core-web-vitals","tag-frontend","tag-india","tag-optimization","tag-speed","tag-web-performance"],"_links":{"self":[{"href":"https:\/\/softcrony.com\/blog\/wp-json\/wp\/v2\/posts\/146","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/softcrony.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/softcrony.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/softcrony.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/softcrony.com\/blog\/wp-json\/wp\/v2\/comments?post=146"}],"version-history":[{"count":0,"href":"https:\/\/softcrony.com\/blog\/wp-json\/wp\/v2\/posts\/146\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/softcrony.com\/blog\/wp-json\/wp\/v2\/media\/148"}],"wp:attachment":[{"href":"https:\/\/softcrony.com\/blog\/wp-json\/wp\/v2\/media?parent=146"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/softcrony.com\/blog\/wp-json\/wp\/v2\/categories?post=146"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/softcrony.com\/blog\/wp-json\/wp\/v2\/tags?post=146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}