A slow website does not automatically need a new framework or a full rewrite. Often, the biggest delays come from a few fixable bottlenecks: oversized images, unnecessary JavaScript, inefficient caching, or a slow server response. A rewrite can introduce new risks without addressing the underlying cause.
This guide shows how to measure performance, identify the work that matters most, and make focused improvements while keeping your existing application intact.
Key takeaways
- Measure real user experience and controlled lab performance before changing code.
- Use Core Web Vitals to understand loading, responsiveness, and visual stability.
- Optimize the largest and most visible images before applying blanket image changes.
- Reduce JavaScript that users do not need, and load noncritical code only when appropriate.
- Configure caching for the lifetime of each resource, not with one rule for every URL.
- Prioritize changes by user impact, evidence, implementation effort, and risk.
Start with measurements, not assumptions
Performance work is most effective when it begins with a repeatable baseline. A page can feel slow because of network latency, server response time, render-blocking resources, large images, JavaScript execution, or an interaction that takes too long to respond. These problems need different fixes.
Use both field data and lab measurements where possible:
- Field data reflects experiences from real users across devices, locations, and network conditions. It helps show whether a problem affects visitors in practice.
- Lab data comes from a controlled test, such as a browser performance audit. It helps reproduce a problem and inspect its causes, but one run is not a complete picture of every user's experience.
Choose representative pages and workflows: for example, a landing page, a product listing, and a form submission. Record the page, device or test conditions, and result for each run. Repeat tests when results vary. Compare like with like; changing the test device or network can make before-and-after results misleading.
Browser developer tools can help investigate network requests, JavaScript execution, layout shifts, and rendering. Lighthouse can provide a repeatable lab audit, while field data can help validate whether a lab finding reflects real visitors. Treat scores as diagnostic signals, not as the goal by themselves.
Use Core Web Vitals as user-focused signals
Core Web Vitals focus on loading, responsiveness, and visual stability. The commonly used good thresholds, assessed at the 75th percentile of page visits, are:
| Metric | What it describes | Good threshold |
|---|---|---|
| Largest Contentful Paint (LCP) | How quickly the main visible content loads | 2.5 seconds or less |
| Interaction to Next Paint (INP) | How promptly the page responds to interactions | 200 milliseconds or less |
| Cumulative Layout Shift (CLS) | How much visible content shifts unexpectedly | 0.1 or less |
A site can have good lab results and still have poor field results, or the reverse. Consider the page-level and device-level context, and investigate the experience behind a metric rather than trying to optimize a number in isolation.
Find the bottleneck that matters most
Start with the pages and tasks that matter to visitors and the business. Check field measurements, review a lab trace, and inspect the page's network activity. Look for evidence such as:
- A slow initial document response across important pages.
- A large image that dominates the initial viewport or LCP.
- JavaScript files that take significant time to download, parse, or execute.
- Requests that block rendering but are not needed for initial content.
- Layout shifts caused by images, embeds, or late-loading content.
- A slow interaction that triggers expensive work on the main thread.
Prioritize a bottleneck using four questions: How many users does it affect? How severe is the impact? How confident are you in the diagnosis? How much effort and risk will the fix require? A small, well-evidenced change affecting a high-traffic page is often a better first step than a broad refactor based on a hunch.
Optimize images where they have the most impact
Images are often among the largest resources a page downloads. Start with images visible on initial load, especially the main content image. Then review image-heavy pages and below-the-fold content.
Practical improvements include:
- Serve an appropriate size. Avoid sending a large desktop image to a small mobile display when a smaller version will do.
- Use responsive sources when needed. The
srcsetandsizesattributes let the browser choose among available image sizes. - Choose a suitable format. Modern formats such as WebP or AVIF can reduce file size in supported browsers, but retain suitable fallbacks if your delivery setup requires them.
- Set intrinsic dimensions. Width and height attributes help browsers reserve space and reduce layout shifts.
- Lazy-load offscreen images. This can defer work that is not needed for the initial viewport. Do not apply lazy loading indiscriminately to the main image that should load promptly.
For example, a responsive image might look like this:
<img
src="/images/team-800.webp"
srcset="/images/team-400.webp 400w, /images/team-800.webp 800w"
sizes="(max-width: 600px) 100vw, 800px"
width="800"
height="533"
alt="The support team working together"
loading="lazy"
>
Use loading="lazy" for an image when it is not needed near the top of the page. For a prominent initial image, test whether eager loading or an appropriate preload improves its discovery and loading; avoid preloading resources without evidence, since extra high-priority requests can compete for bandwidth.
Reduce JavaScript users do not need
A large JavaScript bundle can delay rendering and make interactions less responsive, particularly on slower devices. Inspect what is actually included before changing the build. Bundle analyzers and browser performance traces can reveal duplicated packages, unused code, and expensive execution.
Consider these targeted changes:
- Remove unused dependencies or replace a heavy dependency when a smaller alternative meets the same needs.
- Use route-level or feature-level code splitting so visitors do not download every feature on the first page.
- Load optional features, such as a complex editor or chart, only when a user needs them.
- Defer noncritical scripts and avoid running expensive work during initial rendering.
- Check whether third-party scripts are necessary; each can add network, execution, and reliability costs.
- Preserve functionality and accessibility when changing how or when code loads.
Code splitting is not automatically beneficial everywhere. Splitting can create more requests and add loading transitions, while a large shared bundle can make every page pay for code it does not use. Measure the affected route and test the real user flow after making a change.
Set caching rules that match each resource
Caching can reduce repeat downloads and server work, but the correct policy depends on whether a resource changes and how quickly updates must reach users.
A common approach is to give versioned static assets—such as files with content hashes in their names—a long cache lifetime, because a changed file receives a new URL. HTML documents often need a shorter lifetime or revalidation so visitors can discover updates. APIs and personalized responses need policies appropriate to their data and privacy requirements.
For example, a fingerprinted static asset could use a policy like this:
Cache-Control: public, max-age=31536000, immutable
Use this only when the asset URL changes whenever its content changes. If a file at the same URL is replaced while browsers are told to keep it for a long time, visitors may continue seeing the old version. Do not apply public caching to personalized or sensitive responses without carefully reviewing the consequences. A content delivery network can help serve cacheable resources closer to users, but it does not compensate for incorrect cache headers or an inefficient page.
Improve responsiveness and avoid visual shifts
If a page loads quickly but interactions feel delayed, inspect the work triggered by those interactions. Long-running JavaScript can occupy the main thread and prevent the browser from responding promptly. Break up expensive tasks where practical, avoid unnecessary repeated rendering, and move suitable computation off the main thread when the complexity justifies it.
To reduce unexpected layout shifts, reserve space for images, video, ads, and other content that loads later. Use stable dimensions or an aspect ratio, and avoid inserting content above existing content after a visitor begins reading unless the layout has reserved room for it.
A practical improvement workflow
Use a small, controlled loop rather than changing many things at once:
- Choose a representative page. Start with one that has meaningful traffic or a clear user problem.
- Capture a baseline. Record field data if available, run repeatable lab tests, and inspect the network and performance traces.
- Write down the suspected bottleneck. Tie the diagnosis to evidence, such as image bytes, script execution, or a slow response.
- Make one focused change. Keep the change narrow enough that its effect can be evaluated.
- Retest the same scenario. Use comparable conditions and check both performance and functionality.
- Roll out carefully. Watch relevant field metrics, errors, and user flows after deployment.
- Keep or revert based on evidence. A change that improves one metric but harms another may not be a net improvement.
Keep notes on the tested page, change, test conditions, and results. This makes regressions easier to find and helps prevent teams from repeating experiments that did not work.
Common mistakes to avoid
- Optimizing a score without identifying the cause. A higher audit score is not a substitute for checking real user experience.
- Making several large changes at once. It becomes difficult to know which change helped or caused a regression.
- Lazy-loading the most important image. Deferring the image that establishes the main content can delay its display.
- Preloading everything. Excessive preload hints compete for bandwidth and can delay more important resources.
- Caching every response the same way. HTML, versioned assets, APIs, and personalized pages have different freshness and privacy needs.
- Removing code based only on bundle size. A smaller download can still result in poor interaction performance or broken functionality.
- Treating one lab run as definitive. Results can vary, and lab conditions do not represent every visitor.
- Rewriting before profiling. A new architecture may be costly and still leave the dominant bottleneck untouched.
Conclusion
You can often improve website performance without rewriting the application. Establish a baseline, find the largest user-facing bottleneck, make a focused change, and measure the result under comparable conditions. Image delivery, JavaScript execution, caching, and layout stability are productive areas to investigate, but the right priority depends on evidence from your own pages and visitors. A measured series of small improvements is safer and more informative than a rewrite based on assumptions.
