<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged web-performance</title>
<link>https://coderlegion.com/tag/web-performance</link>
<description>Powered by Question2Answer</description>
<item>
<title>Top No-Code Form Builders to Capture Leads on Blogging Sites</title>
<link>https://coderlegion.com/30949/top-no-code-form-builders-to-capture-leads-on-blogging-sites</link>
<description>&lt;p&gt; A blog can attract readers through search engines, social media, and referrals without generating a single business inquiry. The problem is not always a lack of traffic. Visitors need a clear way to subscribe to a newsletter, request a service, download a resource, or contact the publisher.&lt;/p&gt;
&lt;p&gt;No-code form builders solve this problem by allowing bloggers to create lead capture forms without writing code. Depending on the platform, these tools can collect email addresses, organize customer inquiries, connect submissions to marketing software, and send notifications when someone completes a form.&lt;/p&gt;
&lt;p&gt;The right tool depends on how a blog operates. A WordPress publisher may prefer a form plugin that works inside the dashboard, while a blogger using Ghost, Webflow, or another website platform may benefit from an embeddable form service. Businesses that need a customer relationship management system (CRM) may also want a builder that sends contact information directly into their sales database.&lt;/p&gt;
&lt;p&gt;Here are seven no-code form builders worth considering for lead generation on blogging websites.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;HubSpot Forms: Best for Managing Leads in a CRM&lt;br&gt;
HubSpot Forms is a strong option for bloggers who want to do more than collect email addresses. Its form builder connects lead capture with contact management, allowing submitted information to enter HubSpot's CRM.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The platform provides a drag-and-drop editor and templates for use cases such as newsletter subscriptions, ebook downloads, event registrations, and other lead generation activities. HubSpot also supports standalone forms and forms published on websites. Official HubSpot documentation explains these capabilities.&lt;/p&gt;
&lt;p&gt;For a blog that publishes marketing guides, business tutorials, or industry research, this integration can simplify the process of turning readers into contacts. A visitor who requests a downloadable guide can become a recorded contact rather than an entry that remains isolated in a form builder.&lt;/p&gt;
&lt;p&gt;HubSpot also offers follow-up capabilities, including automated emails and lead nurturing features. The availability of specific automation and personalization functions depends on the subscription and product features involved.&lt;/p&gt;
&lt;p&gt;Best use cases: Business blogs, marketing publications, consultancy websites, and publishers that want to build a contact database.&lt;/p&gt;
&lt;p&gt;Main advantage: Form submissions can feed directly into HubSpot's CRM, reducing the need to transfer contact information manually.&lt;/p&gt;
&lt;p&gt;What to consider: HubSpot's wider marketing features may be unnecessary for a small blog that only needs a basic newsletter signup. Review the available plan limits and the requirements of any automation you intend to use before committing.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;WPForms: Best for WordPress Blogs&lt;br&gt;
WPForms is a WordPress form plugin designed for users who want to create forms through a visual editor. It supports contact forms, lead generation forms, and other data collection use cases without requiring users to build the forms from scratch.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Its Lead Forms addon allows a form to present questions one at a time, using page breaks to divide the submission process into smaller steps. WPForms also documents features such as conditional logic, spam protection, entry management, notifications, and integrations with email marketing services. See the official WPForms Lead Forms documentation.&lt;/p&gt;
&lt;p&gt;For bloggers, the main benefit is the ability to manage forms within the WordPress environment. A publisher can create a newsletter signup form, add a contact form to a service page, or collect inquiries from readers interested in a product recommendation.&lt;/p&gt;
&lt;p&gt;Consider a blog about personal finance that publishes a guide to budgeting. The publisher could place a form on the page offering a downloadable budget planner in exchange for an email address. The form becomes a direct route from the article to the publisher's mailing list, provided the necessary email marketing integration and subscription settings are configured.&lt;/p&gt;
&lt;p&gt;WPForms is particularly relevant to bloggers who want control over form placement and submission management without maintaining a separate form-building account for every basic website form.&lt;/p&gt;
&lt;p&gt;Best use cases: WordPress blogs, affiliate websites, professional blogs, and publishers that collect reader inquiries.&lt;/p&gt;
&lt;p&gt;Main advantage: The plugin works within WordPress and provides a visual form-building process.&lt;/p&gt;
&lt;p&gt;What to consider: Some lead generation features and integrations depend on the relevant addon or paid plan. Confirm which license includes the functions you need before purchasing.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tally: Best for Bloggers Seeking a Generous Free Plan&lt;br&gt;
Tally is an online form builder that offers a free plan with unlimited forms and submissions, subject to its fair-use guidelines. Its listed features include conditional logic, file uploads, payment collection, custom thank-you pages, email notifications, and integrations with services such as Google Sheets, Notion, Airtable, Zapier, and Make. Tally's official pricing page lists its current free-plan offering.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One of Tally's useful features for bloggers is its website embedding support. Publishers can embed forms directly into web pages, display them in popups, or use full-page embeds. Tally documents these options in its official embedding guide.&lt;/p&gt;
&lt;p&gt;This makes the service suitable for bloggers who do not want to install a dedicated WordPress plugin or maintain a complex marketing platform. A publisher can build a reader survey, a newsletter interest form, or a request form for a free resource and then place it within a relevant article.&lt;/p&gt;
&lt;p&gt;For example, a travel blogger could embed a form in a destination guide and ask readers to subscribe for future itinerary recommendations. A technology blogger could collect questions from readers who want a follow-up tutorial on a specific topic.&lt;/p&gt;
&lt;p&gt;Tally can also connect form submissions to external tools, helping publishers organize responses and route information to other applications. Its integration options are useful when a blog's email marketing or customer management system sits outside the form builder.&lt;/p&gt;
&lt;p&gt;Best use cases: New bloggers, independent publishers, niche websites, and creators who want to test lead capture before paying for a dedicated service.&lt;/p&gt;
&lt;p&gt;Main advantage: The free plan includes unlimited forms and submissions, subject to fair-use guidelines.&lt;/p&gt;
&lt;p&gt;What to consider: Some advanced features, including removing Tally branding, require a paid plan. Check the current plan details if branding control is important to your website.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Typeform: Best for Interactive Lead Capture&lt;br&gt;
Typeform is designed for forms that present questions in an interactive format. Rather than relying exclusively on a conventional list of fields, publishers can build a guided question-and-answer experience.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach can be useful when a blogger needs more information than a name and email address. A marketing blog, for example, could ask visitors about their business size, main challenge, and preferred service before requesting contact details.&lt;/p&gt;
&lt;p&gt;Typeform supports website embedding in several formats, including standard, full-page, popup, slider, popover, and side-tab presentations. Its official embedding documentation explains the available options.&lt;/p&gt;
&lt;p&gt;The platform also provides integrations with other applications. Its documentation identifies services such as Mailchimp and Airtable among the integrations available on the Free plan, although the number of responses and other restrictions depend on the selected subscription. See Typeform's plan guidance.&lt;/p&gt;
&lt;p&gt;For a blog that sells consulting services or publishes specialized industry advice, interactive forms can help qualify inquiries before a conversation begins. The publisher can use answers to identify the type of assistance a visitor wants and direct the submission to the appropriate follow-up process.&lt;/p&gt;
&lt;p&gt;However, more questions do not automatically produce better leads. Every additional field creates another task for the visitor. Use conditional logic where available and ask only for information that serves a clear purpose.&lt;/p&gt;
&lt;p&gt;Best use cases: Consulting blogs, business publications, niche expert websites, and publishers that need more detailed reader information.&lt;/p&gt;
&lt;p&gt;Main advantage: Interactive forms and multiple embedding options provide flexibility in how visitors submit information.&lt;/p&gt;
&lt;p&gt;What to consider: Typeform limits responses according to the selected plan. Its free offering is intended for limited use, so check the current response allowance before placing a form on a high-traffic blog.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Jotform: Best for Blogs That Need Versatile Forms&lt;br&gt;
Jotform is a general-purpose online form builder with a visual editor, form templates, integrations, and several publishing options. It is suitable for bloggers who need to collect more than newsletter subscriptions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A publisher might use Jotform to collect guest post proposals, organize event registrations, receive reader feedback, or gather inquiries from prospective clients. Its range of form fields also supports more detailed submissions than a basic email signup.&lt;/p&gt;
&lt;p&gt;Jotform supports embedding through methods including JavaScript and iframe code. It also provides publishing options for website platforms and content management systems, including WordPress, Wix, Squarespace, Ghost, and Blogger. The appropriate option depends on the website and the form's requirements. Jotform's official embedding guide explains the available methods.&lt;/p&gt;
&lt;p&gt;The JavaScript embed can reflect changes made in the form builder without requiring the publisher to replace the original embed code. Jotform's documentation also describes iframe embedding as an alternative when a site experiences conflicts with other scripts.&lt;/p&gt;
&lt;p&gt;This flexibility can be helpful for bloggers who operate several websites or publish through different platforms. Instead of building a separate form from scratch for every use case, they can create forms for specific tasks and publish them where needed.&lt;/p&gt;
&lt;p&gt;Jotform offers a Starter plan, along with paid tiers that increase available limits. Its free plan includes standard features but applies restrictions to usage, and forms include Jotform branding until the user upgrades. Consult the official Jotform pricing page for the current limits.&lt;/p&gt;
&lt;p&gt;Best use cases: Multi-purpose blogs, event-focused publications, professional service websites, and publishers collecting detailed inquiries.&lt;/p&gt;
&lt;p&gt;Main advantage: Multiple embed methods and a broad range of form-building features.&lt;/p&gt;
&lt;p&gt;What to consider: Review submission limits, storage allowances, branding restrictions, and the features available under the plan you intend to use.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Formidable Forms: Best for Advanced WordPress Lead Forms&lt;br&gt;
Formidable Forms is another WordPress form plugin, with features that extend beyond basic contact forms. Its official feature listings include conditional logic, multi-page forms, file uploads, payment forms, surveys, and tools for displaying submitted information on a website. See the Formidable Forms WordPress plugin listing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For bloggers, these capabilities are useful when a form needs to collect structured information. A business blog might collect project requirements from prospective clients, while a property publication could use forms to gather detailed inquiries from readers.&lt;/p&gt;
&lt;p&gt;The plugin also supports integrations with email marketing services. Its paid plans list additional integrations, automation capabilities, and tools for more advanced form-based applications. Available features depend on the license selected.&lt;/p&gt;
&lt;p&gt;A practical example is a website that publishes tutorials for freelancers. The publisher could build a project inquiry form with fields for service type, budget range, deadline, and contact information. Conditional logic could display relevant questions based on the service selected, where the selected plan supports that functionality.&lt;/p&gt;
&lt;p&gt;Formidable Forms is worth considering when the blog needs to do more with submissions than simply forward an email notification. Its data display features can also support websites that need to publish or organize information collected through forms.&lt;/p&gt;
&lt;p&gt;Best use cases: WordPress business blogs, professional directories, service providers, and websites with complex data collection requirements.&lt;/p&gt;
&lt;p&gt;Main advantage: Advanced form logic and options for working with submitted information.&lt;/p&gt;
&lt;p&gt;What to consider: Its broader feature set may be unnecessary for a blog that only needs a simple newsletter signup. Check the current license details for integrations and advanced features before buying.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Mailchimp Embedded Signup Forms: Best for Growing an Email Newsletter&lt;br&gt;
Mailchimp is worth considering for bloggers whose primary goal is to build an email subscriber list. Its embedded signup forms allow publishers to collect email addresses directly on their websites and send subscriber information to a selected Mailchimp audience.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The platform lets users customize form fields, adjust field order, add help text, and generate embed code for their websites. Mailchimp documents support for embedding signup forms on platforms including WordPress, Blogger, and Weebly. Read Mailchimp's official embedded signup form guide.&lt;/p&gt;
&lt;p&gt;This makes Mailchimp useful for blogs that publish recurring content and want a direct channel for reaching readers. A food blogger, for example, could place a subscription form below recipes and invite visitors to receive future recipe updates. A technology publisher could collect subscribers interested in software tutorials or product comparisons.&lt;/p&gt;
&lt;p&gt;Mailchimp also provides options for tracking where subscribers joined an audience. Publishers can use separate forms or tags to identify signups from different website pages, helping them understand which content attracts subscribers. Some tracking configurations require additional setup. See Mailchimp's documentation on identifying signup locations.&lt;/p&gt;
&lt;p&gt;There is one important limitation. Mailchimp's documentation notes that embedded form code may need to be replaced after changes are made to the form. Publishers should also test the form on their live website to verify that submissions and confirmation messages work correctly.&lt;/p&gt;
&lt;p&gt;Best use cases: Newsletter-focused blogs, food websites, lifestyle publishers, and content creators who want to build an email audience.&lt;/p&gt;
&lt;p&gt;Main advantage: Signup forms connect directly to a Mailchimp audience, keeping subscriber collection close to the email marketing process.&lt;/p&gt;
&lt;p&gt;What to consider: Some customization requires editing code, and available marketing features depend on the selected plan. Review the current subscription terms before choosing Mailchimp.&lt;/p&gt;
&lt;p&gt;How to Choose the Right No-Code Form Builder for Your Blog&lt;br&gt;
Before installing a plugin or embedding a third-party form, identify the exact information you need to collect and what should happen after a visitor submits it.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Define the lead you want to capture&lt;br&gt;
A newsletter subscriber is different from a prospective client. Newsletter forms often need only an email address and, where appropriate, a first name. A service inquiry may require additional information to determine whether the request is relevant.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Do not collect personal information without a clear reason. Shorter forms are generally easier to complete, and unnecessary questions can discourage submissions.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check compatibility with your publishing platform&lt;br&gt;
WordPress users can consider WPForms and Formidable Forms. Bloggers using other platforms can evaluate embeddable options such as Tally, Typeform, and Jotform.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Before publishing, verify that your website editor permits the required embed method. Test the form on the live page, not only inside the builder's preview.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Confirm where submissions will go&lt;br&gt;
A form is useful only if someone can access and act on the information it collects. Determine whether submissions should remain in the form builder, enter a spreadsheet, join an email marketing list, or go into a CRM.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Verify that the necessary integration is available on your chosen plan. If the integration requires a paid subscription, include that expense in your decision.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Review usage limits and total cost&lt;br&gt;
Free plans can be useful for testing a new lead generation strategy. However, limits on submissions, branding, integrations, and automation may become important as the blog grows.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Compare the features included in each plan rather than relying on a headline price. Check billing terms and renewal prices before purchasing.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Test the form on mobile devices&lt;br&gt;
Readers may arrive through mobile search results or social media links. Check that the form fits the page, its fields are readable, and the submit button works correctly on smaller screens.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Also test the confirmation message, notification delivery, and any connected email marketing or CRM system. A successful submission should reach the intended destination.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Place forms where they serve the article&lt;br&gt;
A form should relate to the content surrounding it. A recipe blog could offer a newsletter featuring new recipes, while a technology blog could invite readers to receive additional tutorials.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Relevant forms give visitors a clear reason to subscribe or make an inquiry. Avoid placing intrusive forms over the content before readers have had a reasonable opportunity to engage with the page.&lt;/p&gt;
&lt;p&gt;Final Verdict&lt;br&gt;
The best no-code form builder for a blogging website depends on its publishing platform, lead generation goals, budget, and follow-up requirements.&lt;/p&gt;
&lt;p&gt;HubSpot Forms is a strong choice for publishers who want form submissions connected to a CRM. WPForms is a practical option for WordPress bloggers who need a visual form builder within their existing website. Tally is worth considering for creators seeking a free starting point with unlimited forms and submissions under its fair-use guidelines.&lt;/p&gt;
&lt;p&gt;Typeform suits publishers who want interactive questionnaires, while Jotform offers versatile form creation and publishing options. Formidable Forms is better suited to WordPress websites that need advanced logic or more sophisticated ways to use submitted data.&lt;/p&gt;
&lt;p&gt;Start with the simplest tool that meets your requirements. Build one form around a specific goal, connect it to the correct destination, test the entire submission process, and monitor the results. Upgrade only when a documented limitation prevents the blog from carrying out its lead generation strategy.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30949/top-no-code-form-builders-to-capture-leads-on-blogging-sites</guid>
<pubDate>Tue, 13 Oct 2026 15:04:13 +0000</pubDate>
</item>
<item>
<title>Fixing Zero-Click Technical Articles</title>
<link>https://coderlegion.com/30162/fixing-zero-click-technical-articles</link>
<description>&lt;h3 id=&quot;technical-context-and-real-problem&quot;&gt;Technical Context and Real Problem&lt;/h3&gt;
&lt;p&gt;In technical documentation, engineering blogs, and developer hubs, content teams routinely fall victim to the page-one zero-click paradox. An article achieves top-ten ranking positions in &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/google-search-console-content-prioritization/&quot;&gt;Google Search Console&lt;/a&gt; with strong impression volume, yet registers near-zero click-through rates. During an empirical audit of RayLabs across production technical articles, data revealed a sharp disconnect between ranking power and reader conversion.&lt;/p&gt;
&lt;p&gt;Authors naturally write titles from a taxonomic, librarian perspective. However, a developer whose integrated development environment lost its connection does not search for abstract disconnection analysis. They search for the exact symptom observed on their physical device. When a command-line tool fails, developers copy-paste terminal output directly into search engines. Because standard meta titles omit literal error strings, users skip the snippet in favor of forum answers containing the exact phrase. Addressing this mismatch requires categorizing search queries into distinct developer intent archetypes and adjusting title architecture accordingly.&lt;/p&gt;
&lt;h3 id=&quot;architectural-decision-and-trade-offs&quot;&gt;Architectural Decision and Trade-offs&lt;/h3&gt;
&lt;p&gt;To bridge the gap between search impressions and developer clicks, technical content creators can apply the developer intent title framework, which sorts queries into specific archetypes.&lt;/p&gt;
&lt;h4 id=&quot;the-three-developer-search-archetypes&quot;&gt;The Three Developer Search Archetypes&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Literal Error String Intent&lt;/strong&gt;: This is triggered by terminal exceptions, exit codes, daemon crashes, or compiler diagnostics. The query pattern relies on exact string matching, such as error codes or missing device notifications. The title architecture should place the literal error string within the first thirty characters, followed by a colon and the pragmatic resolution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Symptom or State Drift Intent&lt;/strong&gt;: This is triggered by background process termination, battery optimization killing daemons, or silent socket drops. The query pattern uses active verb phrases describing unwanted degradation, such as processes keeping turning off or stopping after sleep. The title architecture mirrors the active verb phrase directly rather than converting it into a formal noun.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operational Configuration Intent&lt;/strong&gt;: This is triggered by setting defaults, switching profiles, passing command-line flags, or configuring local environments. The query pattern uses action verbs followed by configuration parameters, such as setting default models or overriding configuration paths. The title architecture leads with the tool name and an actionable imperative command. For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/mastering-list-to-string-conversion-in-mobile-development/&quot;&gt;Mastering List To String Conversion In&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;title-mutation-risk-management&quot;&gt;Title Mutation Risk Management&lt;/h4&gt;
&lt;p&gt;Modifying the title tag of a page that already ranks on page one carries inherent risks, as careless rewrites can strip away legacy keyword anchors and cause algorithmic volatility. To mitigate this risk, the primary entity noun must remain within the first thirty-five characters of the title. Creators should replace passive editorial descriptors with empirical high-impression queries mined directly from search console data. Furthermore, desktop and mobile display limits must remain between fifty and sixty characters to prevent truncation of intent keywords.&lt;/p&gt;
&lt;p&gt;When editorial branding requires a clean literary title on the page canvas while search discovery demands literal error tokens, content teams can use the main title for on-page presentation and the meta title for search engines and schema markup.&lt;/p&gt;
&lt;h4 id=&quot;configuration-example&quot;&gt;Configuration Example&lt;/h4&gt;
&lt;p&gt;To update your HTML metadata and structured data to serve intent-driven titles without changing your visible on-page H1, configure your document header like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;ADB Device Not Found / Not Detected? Fix USB Debugging Fast&amp;lt;/title&amp;gt;
  &amp;lt;meta name=&quot;description&quot; content=&quot;Fix ADB device not found errors quickly with proven USB debugging troubleshooting steps.&quot;&amp;gt;
  &amp;lt;script type=&quot;application/ld+json&quot;&amp;gt;
  {
    &quot;@context&quot;: &quot;https://schema.org&quot;,
    &quot;@type&quot;: &quot;TechArticle&quot;,
    &quot;headline&quot;: &quot;ADB Device Not Found / Not Detected? Fix USB Debugging Fast&quot;,
    &quot;description&quot;: &quot;Fix ADB device not found errors quickly with proven USB debugging troubleshooting steps.&quot;
  }
  &amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;reusable-takeaways&quot;&gt;Reusable Takeaways&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Mine Impressions with Zero Clicks&lt;/strong&gt;: Query search console data for queries with high impressions and near-zero clicks to identify missing user vocabulary.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prioritize Pain Verbs&lt;/strong&gt;: Developers search with active symptom verbs rather than formal catalog nouns.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Front-Load Literal Errors&lt;/strong&gt;: Place exact error strings and codes within the first thirty characters of the metadata title.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforce Character Limits&lt;/strong&gt;: Keep metadata titles between fifty and sixty characters to ensure mobile and desktop readability without truncation.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;conclusion-and-next-steps&quot;&gt;Conclusion and Next Steps&lt;/h3&gt;
&lt;p&gt;Developers search with pain verbs and literal error messages rather than catalog nouns. By mining impressions with zero clicks in search console data and enforcing character boundaries, technical writers can capture targeted developer traffic reliably. Review your top-performing low-CTR pages, adjust their meta titles to reflect true search intent, and monitor performance shifts over a fourteen-day window.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30162/fixing-zero-click-technical-articles</guid>
<pubDate>Wed, 07 Oct 2026 18:04:14 +0000</pubDate>
</item>
<item>
<title>Programmatic SEO Tag Density and Entity Hubs</title>
<link>https://coderlegion.com/29807/programmatic-seo-tag-density-and-entity-hubs</link>
<description>&lt;p&gt;Blogging platforms and content management systems historically popularized tag taxonomies by automatically generating a dedicated archive route for every tag applied to a post. While this appeared to expand the site indexable surface, in production environments it routinely causes severe technical SEO regressions. On RayLabs, an audit revealed 291 unique tags across 195 approved articles, but 201 tags or 69 percent were singletons attached to only a single post. Generating public &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/engineering-high-intent-activity-landing-pages/&quot;&gt;landing pages&lt;/a&gt; for singletons creates low-value doorway pages that search engine crawlers classify as soft 404 or thin content, diluting overall site domain authority.&lt;/p&gt;
&lt;p&gt;When a site maintains broad topical pillars such as android-mobile or developer-tools, creating redundant tag pages like android-mobile or developer-tools causes severe internal competition in search engine result pages, splitting backlinks and confusing search crawler intent. Furthermore, search engine bots spend valuable crawl budget fetching hundreds of near-identical single-article tag pages instead of indexing newly published, high-value technical guides. To solve these issues while capturing long-tail programmatic keyword benefits, RayLabs engineered a deterministic density threshold gate.&lt;/p&gt;
&lt;h3 id=&quot;implementing-the-density-threshold-gate&quot;&gt;Implementing the Density Threshold Gate&lt;/h3&gt;
&lt;p&gt;The architecture relies on empirical density thresholds and strict routing rules to separate high-value entity hubs from thin content traps. Only tags associated with three or more approved canonical articles, or explicitly defined in a curated metadata registry, qualify to generate static HTML landing pages at the tags path. Singletons and low-density tags with fewer than three articles never generate static landing pages and are excluded from the XML sitemap. Instead of breaking or dead-ending, clicking a thin tag dynamically routes the user to a filtered catalog search query, providing excellent search UX without creating indexable crawler traps.&lt;/p&gt;
&lt;p&gt;Before evaluating tag density, the taxonomy router checks the topic slug. Any tag matching a canonical pillar topic automatically resolves to the topics path. This enforces a single authoritative canonical source for core themes. Rather than relying on naive title-casing which mangles technical terms, the engine maintains an exact-brand dictionary for terms like CI/CD, Jetpack Compose, CameraX, WorkManager, Room Database, macOS, and SEO. For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/audit-macos-system-data-before-deleting-developer-caches/&quot;&gt;Audit Macos System Data Before Deleting&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;structured-data-and-internal-linking-mesh&quot;&gt;Structured Data and Internal Linking Mesh&lt;/h3&gt;
&lt;p&gt;Every qualified hub is enriched with human-crafted technical descriptions defining the engineering scope of the entity. Pages render valid schema collection page structured data with nested breadcrumb lists and item lists containing all tagged guides. In individual article headers, passive tag labels are transformed into semantic tokenized links pointing directly to the hub. Hubs feature a related sibling tags bar displaying co-occurring high-density tags, creating a tight topical mesh that circulates page rank and reduces bounce rates.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function evaluateTagRouting(tag, articleCount, isPillar) {
  if (isPillar) {
    return { route: `/topics/${tag}/`, type: 'canonical_pillar' };
  }
  if (articleCount &amp;gt;= 3) {
    return { route: `/tags/${tag}/`, type: 'static_hub' };
  }
  return { route: `/articles/?q=${tag}`, type: 'dynamic_fallback' };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;conclusion-and-architectural-summary&quot;&gt;Conclusion and Architectural Summary&lt;/h3&gt;
&lt;p&gt;Preventing search engine penalties from programmatic tags requires moving away from indiscriminate page generation. By enforcing strict minimum article density gates, redirecting overlapping terms to canonical pillars, and turning thin tags into dynamic search fallbacks, you protect crawl budget and maintain high editorial standards across large technical publishing platforms.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29807/programmatic-seo-tag-density-and-entity-hubs</guid>
<pubDate>Mon, 05 Oct 2026 06:04:07 +0000</pubDate>
</item>
<item>
<title>Building a Zero-Jank CSS Logo Marquee</title>
<link>https://coderlegion.com/29674/building-a-zero-jank-css-logo-marquee</link>
<description>&lt;p&gt;Integrating community badges, open-source repositories, and syndicated profiles into static engineering sites often creates unexpected performance bottlenecks. Many teams pull in thirty-kilobyte carousel libraries or JavaScript animation engines just to display moving brand icons. These scripts execute continuously on the main thread, trigger Cumulative Layout Shift, and degrade mobile performance scores. Placing social proof directly below the hero header competes with primary calls-to-action, while brittle third-party API timeouts during static site generation frequently cause builds to fall back to hardcoded snapshots containing deleted or renamed repositories. For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/integrating-open-source-ai-tooling-into-android-ci-pipelines/&quot;&gt;Integrating Open Source Ai Tooling Ci&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To resolve these issues without compromising speed, accessibility, or build determinism, you can engineer a lightweight, CSS-only ambient syndication architecture. This approach replaces heavy scripts with a GPU-accelerated ticker, ensures strict compliance with reduced-motion preferences, and implements a deterministic fallback pipeline.&lt;/p&gt;
&lt;h3 id=&quot;the-mathematics-of-seamless-css-marquees&quot;&gt;The Mathematics of Seamless CSS Marquees&lt;/h3&gt;
&lt;p&gt;Instead of relying on JavaScript animation loops that recalculate layout during frames, the marquee runs purely on hardware-accelerated transforms using &lt;code&gt;transform: translateX()&lt;/code&gt;. Achieving a seamless infinite loop without visual popping requires careful item duplication. If your dataset contains $N$ platforms, repeating the array four times yields $4N$ items in the track. Translating the track container by &lt;code&gt;calc(-50% - 1.75rem)&lt;/code&gt; means that exactly $2N$ items cycle before snapping invisibly back to the origin.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.marquee-container {
  overflow: hidden;
  width: 100%;
  mask-image: linear-gradient(to right, transparent 0%, black 7%, black 93%, transparent 100%);
}

.marquee-track {
  display: flex;
  width: max-content;
  animation: scroll-ticker 35s linear infinite;
}

.marquee-track:hover {
  animation-play-state: paused;
}

@keyframes scroll-ticker {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(calc(-50% - 1.75rem));
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rather than placing opaque pseudo-elements over the edges of the ticker, which can block pointer events and break on varied backgrounds, the code uses &lt;code&gt;mask-image: linear-gradient()&lt;/code&gt;. This applies true alpha clipping at the container edges, allowing seamless fading while preserving full interactivity for underlying elements.&lt;/p&gt;
&lt;h3 id=&quot;accessibility-and-state-management&quot;&gt;Accessibility and State Management&lt;/h3&gt;
&lt;p&gt;Static and moving components must remain usable across input modalities and user preferences. By default, marquee items appear in a muted monochrome state with reduced opacity to prevent visual distraction. Hovering or focusing an item pauses the animation track, slightly scales the target element, and restores its full brand identity. For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/prevent-private-urls-leaking-static-site-output/&quot;&gt;Prevent Private Urls Leaking Static Site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To respect user vestibular motion preferences, a media query disables the animation entirely and falls back to a clean flex-wrap layout:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;@media (prefers-reduced-motion: reduce) {
  .marquee-track {
    animation: none;
    flex-wrap: wrap;
    justify-content: center;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Furthermore, all decorative elements are hidden from screen readers using &lt;code&gt;aria-hidden=&quot;true&quot;&lt;/code&gt;, and interactive containers maintain touch targets of at least forty-four pixels.&lt;/p&gt;
&lt;h3 id=&quot;resilient-static-site-generation-ingestion&quot;&gt;Resilient Static Site Generation Ingestion&lt;/h3&gt;
&lt;p&gt;When fetching dynamic third-party data during static site generation, tight timeouts and unvalidated mock responses can corrupt your production output. If an API request times out and falls back to stale data containing deleted repositories, those ghost entities get permanently baked into your output HTML.&lt;/p&gt;
&lt;p&gt;To prevent this, extend your fetch timeout boundary to at least thirty-five hundred milliseconds with proper authorization headers, and subject your fallback dataset to the same rigorous validation as your live API responses. Purge deprecated records regularly during build configuration updates.&lt;/p&gt;
&lt;h3 id=&quot;summary-of-architectural-decisions&quot;&gt;Summary of Architectural Decisions&lt;/h3&gt;
&lt;p&gt;Replacing heavy JavaScript sliders with GPU-accelerated CSS transforms and robust static ingestion pipelines eliminates layout shifts while keeping your codebase maintainable. By relying on native browser layout compositing, proper alpha masking, and strict build-time validation, you can showcase community contributions reliably without sacrificing site performance.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29674/building-a-zero-jank-css-logo-marquee</guid>
<pubDate>Sun, 04 Oct 2026 00:11:39 +0000</pubDate>
</item>
<item>
<title>Build a Lightweight CSS Marquee Without Damaging Web Vitals</title>
<link>https://coderlegion.com/29589/build-a-lightweight-css-marquee-without-damaging-web-vitals</link>
<description>&lt;p&gt;Infinite tickers and scrolling marquees are a frequent requirement for showcasing partner logos, engineering badges, or featured announcements on modern marketing sites. Yet, many implementations rely on third-party JavaScript libraries or heavy animation wrappers that degrade performance. When a marquee is driven by JavaScript style updates on every frame, it blocks the main thread, inflates Total Blocking Time, and triggers costly layout recalculations. Developers need a way to create continuous visual movement that remains buttery smooth without sacrificing user experience or performance scores. For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/android-app-updates-play-vs-managed-devices/&quot;&gt;App Updates Play Vs Managed Devices&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-performance-cost-of-javascript-marquees&quot;&gt;The Performance Cost of JavaScript Marquees&lt;/h3&gt;
&lt;p&gt;Traditional ticker scripts rely on &lt;code&gt;requestAnimationFrame&lt;/code&gt; loops or timer intervals to mutate element positions on every tick. This approach forces the browser to recalculate styles and geometry during the layout phase for every active frame. On mobile devices or lower-powered hardware, this constant recalculation drops frame rates below sixty frames per second and elevates Total Blocking Time. Furthermore, dynamic DOM node insertion during script initialization can trigger sudden content jumps, ruining Cumulative Layout Shift metrics before the page fully settles. For a related implementation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/android-memory-optimization-low-ram-devices/&quot;&gt;Memory Optimization Low Ram Devices&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-double-track-duplication-pattern&quot;&gt;The Double-Track Duplication Pattern&lt;/h3&gt;
&lt;p&gt;To achieve a seamless infinite loop without JavaScript calculations, you can structure the HTML with a parent container and two identical track children. Each track contains the exact same sequence of items. When the container width is set to fit the content of a single track, duplicating that track ensures that as the first set of items scrolls completely out of view, the second set seamlessly takes its place.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div class=&quot;marquee-container&quot;&amp;gt;
  &amp;lt;div class=&quot;marquee-track&quot;&amp;gt;
    &amp;lt;div class=&quot;marquee-group&quot;&amp;gt;
      &amp;lt;span&amp;gt;Acme Corp&amp;lt;/span&amp;gt;
      &amp;lt;span&amp;gt;Beta LLC&amp;lt;/span&amp;gt;
      &amp;lt;span&amp;gt;Gamma Inc&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;marquee-group&quot; aria-hidden=&quot;true&quot;&amp;gt;
      &amp;lt;span&amp;gt;Acme Corp&amp;lt;/span&amp;gt;
      &amp;lt;span&amp;gt;Beta LLC&amp;lt;/span&amp;gt;
      &amp;lt;span&amp;gt;Gamma Inc&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting the second group with &lt;code&gt;aria-hidden=&quot;true&quot;&lt;/code&gt; ensures that screen readers do not read the duplicated items twice, preserving a clean accessibility tree.&lt;/p&gt;
&lt;h3 id=&quot;gpu-compositing-with-3d-transforms&quot;&gt;GPU Compositing with 3D Transforms&lt;/h3&gt;
&lt;p&gt;Performance relies entirely on choosing the right CSS properties to animate. Animating layout properties like &lt;code&gt;margin-left&lt;/code&gt; or &lt;code&gt;left&lt;/code&gt; forces the browser to repeat layout and paint steps continuously. By contrast, using hardware-accelerated transforms shifts the workload entirely to the GPU compositor thread.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.marquee-container {
  overflow: hidden;
  width: 100%;
  display: flex;
}

.marquee-track {
  display: flex;
  width: max-content;
  animation: scroll-marquee 30s linear infinite;
}

.marquee-group {
  display: flex;
  gap: 2rem;
  justify-content: space-around;
  min-width: 100%;
}

@keyframes scroll-marquee {
  0% {
    transform: translate3d(0, 0, 0);
  }
  100% {
    transform: translate3d(-50%, 0, 0);
  }
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the track contains two identical groups side by side, translating the entire track by negative fifty percent moves exactly one full group width before looping back to zero instantly and invisibly.&lt;/p&gt;
&lt;h3 id=&quot;zero-cls-containment&quot;&gt;Zero CLS Containment&lt;/h3&gt;
&lt;p&gt;Layout shifts often occur when dynamic components load fonts or images after initial render. To prevent the marquee container from causing layout instability during hydration, enforce strict CSS containment. By applying explicit height constraints and sizing properties to the container, the browser reserves the exact layout box ahead of time.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.marquee-container {
  contain: layout size;
  height: 4rem;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This containment rule tells the optimization engine that the internal layout of the marquee does not affect outside document elements, eliminating unexpected reflows during load.&lt;/p&gt;
&lt;h3 id=&quot;polishing-with-css-edge-masks&quot;&gt;Polishing with CSS Edge Masks&lt;/h3&gt;
&lt;p&gt;Fading out the left and right edges of a scrolling marquee provides a polished visual finish without adding extra structural DOM nodes or overlapping gradient overlays. The &lt;code&gt;mask-image&lt;/code&gt; property applies a linear alpha gradient directly to the element rendering surface.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.marquee-container {
  mask-image: linear-gradient(
    to right,
    transparent,
    black 15%,
    black 85%,
    transparent
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This composited mask ensures that items fade smoothly into view at the container boundaries, adapting cleanly to any background color or theme.&lt;/p&gt;
&lt;h3 id=&quot;accessibility-and-user-control&quot;&gt;Accessibility and User Control&lt;/h3&gt;
&lt;p&gt;Continuous motion can cause discomfort for users with vestibular disorders. Providing clear ways to pause or disable the animation is both an ethical necessity and a requirement for strict accessibility compliance. You can pause movement on hover or keyboard focus, and completely disable it for users who prefer reduced motion.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.marquee-container:hover .marquee-track,
.marquee-container:focus-within .marquee-track {
  animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
  .marquee-track {
    animation: none;
    transform: none;
    overflow-x: auto;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By adding these rules, users retain full control over their reading experience, and keyboard navigators can interact with the content inside the ticker without struggling against moving targets.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Implementing an infinite ticker no longer requires heavy JavaScript plugins or complex timer loops. By combining a double-track flexbox structure, GPU-accelerated transforms, CSS edge masks, and strict accessibility safeguards, you can deliver smooth visual motion that protects Core Web Vitals and runs efficiently across all devices.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29589/build-a-lightweight-css-marquee-without-damaging-web-vitals</guid>
<pubDate>Sat, 03 Oct 2026 05:14:58 +0000</pubDate>
</item>
<item>
<title>Cloudflare and PSI Automation: Challenges, Allowlists &amp; Monitoring</title>
<link>https://coderlegion.com/27084/cloudflare-and-psi-automation-challenges-allowlists-monitoring</link>
<description>&lt;p&gt;The same client URL opened in PageSpeed Insights from a laptop often completes. Put that URL on a schedule, call the PageSpeed Insights API from a monitoring stack, or re-run Lighthouse through a headless pipeline, and the document request returns a Cloudflare challenge, a 403, or an empty error shell. The score that ends up in the ticket is not a Core Web Vitals regression. It is a security edge deciding that automation is not a visitor worth serving HTML.&lt;/p&gt;&lt;p&gt;That contrast is the whole problem for agencies. Manual checks feel fine, so the account team trusts the green screenshot. Overnight automation red-flags the homepage, clients ask what broke, and engineering digs into LCP subparts on a page that never reached the real document. Cloudflare is doing its job. Monitoring is measuring the wrong response. What follows is how to tell those cases apart, which Cloudflare controls usually cause them, and how to allow legitimate PageSpeed Insights traffic without opening the site to every scraper on the internet.&lt;/p&gt;&lt;p&gt;For the wider network path (DNS, TLS, CDN cache), see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/network-performance-dns-tls-http-cdn-cache-rules&quot;&gt;Network Performance for Web Teams: DNS, TLS, HTTP, CDN, and Cache Rules&lt;/a&gt;. That guide covers where edge routing and cache hits change Time to First Byte before paint work begins. For why one-off PageSpeed Insights runs are a weak substitute for a schedule, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/pagespeed-insights-vs-automated-monitoring-when-manual-checks-arent-enough&quot;&gt;PageSpeed Insights vs Automated Monitoring: When Manual Checks Aren't Enough&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;why-pagespeed-insights-works-in-a-browser-but-fails-under-cloudflare-automation&quot; class=&quot;scroll-mt-24&quot;&gt;Why PageSpeed Insights works in a browser but fails under Cloudflare automation&lt;/h2&gt;&lt;p&gt;PageSpeed Insights and Lighthouse need a full HTML document, then a rendered page, to compute lab metrics. An interactive browser session often looks enough like a human visitor for Cloudflare to pass the first request, especially if cookies from a prior challenge exist, JavaScript challenges can run, or the operator is on a residential network that is not already associated with bulk scraping. Scheduled and API-driven runs typically do not share that context. They arrive from data-centre ranges, use predictable automation User-Agents (including Chrome-Lighthouse strings), lack challenge cookies, and cannot complete managed challenges the way a person with a browser can.&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Path&lt;/th&gt;&lt;th&gt;Typical Cloudflare outcome&lt;/th&gt;&lt;th&gt;What Lighthouse / PageSpeed Insights measures&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Interactive browser PageSpeed Insights / DevTools&lt;/td&gt;&lt;td&gt;Often allowed after challenge or soft pass&lt;/td&gt;&lt;td&gt;Real HTML and assets (when allowed)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PageSpeed Insights API / scheduled headless&lt;/td&gt;&lt;td&gt;Challenge interstitial, 403, connection error&lt;/td&gt;&lt;td&gt;Challenge page, error document, or failed fetch&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Monitoring tool from fixed egress IPs&lt;/td&gt;&lt;td&gt;Blocked until IP Access or Skip rule exists&lt;/td&gt;&lt;td&gt;Same failure modes until allowlisted&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;When the fetch fails, Google’s tooling often surfaces messages such as &lt;code&gt;ERRORED_DOCUMENT_REQUEST&lt;/code&gt; or a generic connection failure. In Cloudflare Security Events you may see Bot Fight Mode, Super Bot Fight Mode, a WAF custom rule, rate limiting, or Under Attack Mode labelled against the same URL. Treating that as an LCP incident wastes a sprint; treat it as automation that never received the origin HTML.&lt;/p&gt;&lt;p&gt;Agencies also hit a quieter failure: the run “succeeds” but the lab screenshot shows a Cloudflare interstitial or a stripped challenge document. Scores look absurdly bad or oddly empty. Field data in PageSpeed Insights can still look normal when enough real users reach the site, which is another reason not to collapse lab and field into one narrative. For missing URL-level field metrics, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/pagespeed-insights-no-cls-inp-for-url&quot;&gt;When PageSpeed Insights Shows No CLS or INP for Your URL&lt;/a&gt;. For when synthetic and real-user signals should disagree on purpose, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/when-to-use-synthetic-vs-real-user-monitoring-performance&quot;&gt;When to Use Synthetic vs Real User Monitoring for Performance&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;which-cloudflare-controls-block-pagespeed-insights-and-lighthouse&quot; class=&quot;scroll-mt-24&quot;&gt;Which Cloudflare controls block PageSpeed Insights and Lighthouse&lt;/h2&gt;&lt;p&gt;Several Cloudflare products can interrupt PageSpeed Insights automation. They are not interchangeable, and some cannot be bypassed with the allowlist patterns people copy from blog posts. Matching the failed run to the right product saves a week of rewriting Largest Contentful Paint budgets on a document that never left the edge.&lt;/p&gt;&lt;h3&gt;Bot Fight Mode (free)&lt;/h3&gt;&lt;p&gt;Cloudflare documents that Bot Fight Mode challenges traffic matching known bot patterns and that you cannot bypass or skip it with WAF custom rules or Page Rules, because it does not run on the Ruleset Engine. If portfolio monitoring is blocked and Bot Fight Mode is on, Skip rules aimed at Chrome-Lighthouse will not save you. Cloudflare’s own guidance points teams toward Super Bot Fight Mode when they need exceptions, or toward IP Access rules that match the connecting IP before Bot Fight Mode evaluates the request.&lt;/p&gt;&lt;h3&gt;Super Bot Fight Mode (Pro and above)&lt;/h3&gt;&lt;p&gt;This product adds configurable actions and supports Skip actions in WAF custom rules. It is the practical path when you need to allow verified bots or monitoring User-Agents while still challenging other automated traffic. Misconfigured “Definitely automated” or “Likely automated” actions still catch Lighthouse and PageSpeed Insights traffic that Cloudflare does not classify as a verified bot.&lt;/p&gt;&lt;h3&gt;Under Attack Mode&lt;/h3&gt;&lt;p&gt;This mode raises friction for almost everyone. Community threads regularly report PageSpeed Insights failing with &lt;code&gt;ERRORED_DOCUMENT_REQUEST&lt;/code&gt; while Under Attack Mode is enabled, even when a User-Agent allowlist appears to match in the logs. Temporary attack posture and continuous performance monitoring do not coexist cleanly unless verified bots (Known Bots) are explicitly skipped from remaining security checks. Under Attack Mode fits active incidents; it is a poor permanent monitoring configuration.&lt;/p&gt;&lt;h3&gt;WAF custom rules and managed rules&lt;/h3&gt;&lt;p&gt;Country blocks, ASN blocks, rate limits, and managed rule sets can 403 Google’s lab infrastructure or your monitoring egress. A rule that challenges “automated” traffic without an earlier Skip for known good bots will catch PageSpeed Insights. Order matters: exceptions for verified bots and monitoring IPs need to run before the block or challenge.&lt;/p&gt;&lt;h3&gt;Rate limiting and Bot Management scores&lt;/h3&gt;&lt;p&gt;Burst schedules across many URLs on one hostname can trip rate limits even when a single manual test works. Enterprise Bot Management customers can use score fields for finer thresholds; most agency clients on Free or Pro still need coarser Skip / IP Access patterns. When you investigate a Cloudflare block on PageSpeed Insights, open Security Events for the exact timestamp of the failed run, note the Service label, and match it to the product above before you rewrite Largest Contentful Paint budgets. Guessing “CDN is slow” from a challenged document wastes time that belongs in the security configuration, so screenshot the event for the client ticket so the zone admin sees the same evidence you used.&lt;/p&gt;&lt;h2 id=&quot;origin-versus-edge-what-a-blocked-lab-run-is-actually-measuring&quot; class=&quot;scroll-mt-24&quot;&gt;Origin versus edge: what a blocked lab run is actually measuring&lt;/h2&gt;&lt;p&gt;A healthy synthetic run should measure the HTML and assets your visitors receive after Cloudflare’s edge has applied caching and routing, not a challenge interstitial and not a bypass that skips the edge entirely unless that bypass is intentional. Mixing those paths without labels is what makes weekly scores look volatile when nothing in the application changed. Three measurement modes show up in agency work:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Edge as users see it. Requests go through Cloudflare with the same security posture as public traffic, except for narrow exceptions for known good lab traffic. This is the default you want for scheduled PageSpeed Insights once allowlists are correct.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Challenge / block response. Automation measures Cloudflare’s security HTML or a failed document fetch. Lab metrics are invalid for Core Web Vitals decisions. Report the failure as a monitoring access issue, not as a performance regression.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Origin-direct bypass. Some teams point monitors at the origin hostname or an IP Access “grey cloud” path so tests always pass. That can prove origin PHP or application latency, but it is not the same as visitor experience through the CDN. Use it as a diagnostic split, labelled clearly, not as the only portfolio score.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;In our experience, the worst client reports skip those labels. One week’s “green” run used a temporary Under Attack exception; the next week’s “red” run hit Bot Fight Mode again. The narrative looks like volatility. The cause is inconsistent access to the document.&lt;/p&gt;&lt;p&gt;Origin and edge stay honest the same way Cache-Control stays honest: name which hostname, which Cloudflare mode, and which allowlist were in effect for the run you are quoting. Pair that with fixed performance budgets so a challenge-induced score does not auto-open a “fix LCP” ticket. Budget templates live in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/performance-budget-thresholds-template&quot;&gt;Performance Budget Thresholds Template&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;allowlists-that-work-verified-bots-ip-access-and-skip-rules&quot; class=&quot;scroll-mt-24&quot;&gt;Allowlists that work: verified bots, IP Access, and Skip rules&lt;/h2&gt;&lt;p&gt;There is no single checkbox labelled “allow PageSpeed Insights forever.” You assemble a small set of exceptions that match how Cloudflare classifies the traffic, then you verify with a scheduled re-run. The patterns below are the ones that usually hold up under Security Events review.&lt;/p&gt;&lt;h3&gt;Prefer verified / known bots over User-Agent string matching alone&lt;/h3&gt;&lt;p&gt;Cloudflare documents custom-rule patterns that allow search-engine and other verified bots using fields such as &lt;code&gt;cf.client.bot&lt;/code&gt; (and Bot Management fields where available). User-Agent contains &lt;code&gt;&quot;Chrome-Lighthouse&quot;&lt;/code&gt; rules are fragile: they are easy to spoof, easy to miss when Google changes strings, and insufficient under Under Attack Mode when the wrong Skip scope is selected. Known Bots / verified bot Skip rules for remaining WAF modules and Super Bot Fight Mode (where the plan supports them) are a stronger starting point, with monitoring-specific exceptions added only if Security Events still show blocks.&lt;/p&gt;&lt;h3&gt;Use IP Access allow rules for known monitoring egress&lt;/h3&gt;&lt;p&gt;If your PageSpeed Insights automation or third-party monitor exits from stable IP ranges, an IP Access allow that matches the connecting IP can prevent Bot Fight Mode from firing on those requests (Cloudflare notes Bot Fight Mode will not trigger when an IP Access rule matches first). Document the ranges with the vendor and rotate them when the vendor publishes changes. Allowlisting entire cloud regions is usually too broad for the risk you are trying to reduce.&lt;/p&gt;&lt;h3&gt;Move from Bot Fight Mode to Super Bot Fight Mode when you need Skip&lt;/h3&gt;&lt;p&gt;Free Bot Fight Mode cannot honour WAF Skip the way Super Bot Fight Mode can. Agencies stuck on Free with constant false positives on monitoring have a product constraint, not a missing regex. The realistic options are to disable Bot Fight Mode after the attack window, upgrade so Skip rules work, or accept that scheduled PageSpeed Insights on that zone will stay unreliable.&lt;/p&gt;&lt;h3&gt;Scope Skip narrowly&lt;/h3&gt;&lt;p&gt;A Skip that disables all remaining security for any bot-shaped request is a self-inflicted outage waiting to happen. Narrowing by hostname path if needed (public marketing URLs versus authenticated app), by verified-bot field, and by known monitoring IPs keeps the exception honest. Review Security Events after the change to confirm PageSpeed Insights is skipped for the right reason and that random scrapers are still challenged.&lt;/p&gt;&lt;h3&gt;Confirm with the same automation that failed&lt;/h3&gt;&lt;p&gt;A manual browser pass after a rule change proves little. Re-run the scheduled job or API call that produced the original error, and only then mark the incident closed. Scheduling discipline for portfolios is covered in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/how-to-schedule-pagespeed-monitoring-test-frequency-priority-portfolio&quot;&gt;PageSpeed Monitoring: How to Schedule Test Frequency and Priority Across Your Portfolio&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;user-agent-myths-warm-up-retries-and-what-not-to-do&quot; class=&quot;scroll-mt-24&quot;&gt;User-Agent myths, warm-up, retries, and what not to do&lt;/h2&gt;&lt;p&gt;Spoofing Googlebot to “pass” Cloudflare from a monitoring host violates Google’s crawler guidelines, teaches the wrong security lesson, and still fails when Cloudflare verifies bots beyond the User-Agent string. If you need Googlebot behaviour, you are not debugging PageSpeed Insights automation; you are debugging crawl access. Turning security off globally to get a green Lighthouse score is the other common shortcut, and it trades one KPI for a real incident risk. Time-boxed Under Attack Mode, narrow Skips, and IP Access for monitors are safer than disabling Bot Fight Mode or WAF managed rules site-wide for a performance project.&lt;/p&gt;&lt;p&gt;User-Agent allowlists help on some plans and some products, but they are not complete. They do not override Bot Fight Mode. They do not always survive Under Attack Mode. They do nothing for rate limits keyed on IP.&lt;/p&gt;&lt;p&gt;Warm-up and retries have limits. A single warm-up request that solves a challenge in a full browser does not automatically mint cookies for a separate PageSpeed Insights API worker. Retrying a challenged URL every minute usually deepens rate-limit pain without producing a valid document, so retries fit transient network errors after allowlists are confirmed, not as a substitute for a Skip rule.&lt;/p&gt;&lt;p&gt;Runbooks stay clearer when “tool blocked” and “site slow” stay separate. Security Events and HTTP status come first; LCP subparts come after. If the document request never returned 200 HTML from the application, the performance playbook stops and a Cloudflare configuration ticket with the client’s zone admin is the right next step.&lt;/p&gt;&lt;h2 id=&quot;how-agencies-should-report-cloudflare-blocks-in-client-monitoring&quot; class=&quot;scroll-mt-24&quot;&gt;How agencies should report Cloudflare blocks in client monitoring&lt;/h2&gt;&lt;p&gt;Clients rarely want a lecture on Ruleset Engine phases. They want to know whether the site is slow for users or whether the monitor was refused. A short script in the report usually covers it:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;State the failure mode in plain language: “Scheduled PageSpeed Insights received a Cloudflare challenge / 403, so lab metrics are invalid for this run.”&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Attach evidence: Security Events screenshot or rule name, HTTP status, and whether field data in PageSpeed Insights still looks normal.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Name the owner: client Cloudflare admin versus agency monitoring egress. Most allowlist changes require the zone owner.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Record the fix: verified-bot Skip, IP Access allow, Super Bot Fight Mode change, or temporary Under Attack Mode turned off after the incident.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Re-run on schedule and only then restore budget alerting for that URL.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Keep synthetic budgets armed for real regressions once access is restored. Muting an entire client because one zone had Bot Fight Mode misconfigured hides unrelated regressions; mute or annotate the affected URLs until the next clean run instead. That keeps Apogee Watcher-style portfolio schedules useful: continuous checks, early detection, clear reports, without pretending every red score is an LCP bug. If you run Apogee Watcher across many Cloudflare-fronted clients, recurring challenge failures belong on the onboarding checklist: confirm Bot Fight Mode posture, document whether Super Bot Fight Mode Skip is available, and store monitoring egress expectations with the client before you promise daily lab history.&lt;/p&gt;&lt;h2 id=&quot;faq&quot; class=&quot;scroll-mt-24&quot;&gt;FAQ&lt;/h2&gt;&lt;h3&gt;Why does PageSpeed Insights work manually but fail on a schedule behind Cloudflare?&lt;/h3&gt;&lt;p&gt;Manual runs often complete challenges or arrive from networks Cloudflare treats more gently. Scheduled API and headless runs look like automation, lack challenge state, and hit Bot Fight Mode, WAF rules, or Under Attack Mode. Fix allowlists and product mode rather than only re-testing from a laptop.&lt;/p&gt;&lt;h3&gt;Can a WAF custom rule Skip Bot Fight Mode for Chrome-Lighthouse?&lt;/h3&gt;&lt;p&gt;No. Cloudflare documents that Bot Fight Mode cannot be bypassed with WAF custom rules or Page Rules. Super Bot Fight Mode with Skip, IP Access allows that match first, or disabling Bot Fight Mode when the attack window is over are the documented paths.&lt;/p&gt;&lt;h3&gt;Should we allowlist by User-Agent or by verified bots?&lt;/h3&gt;&lt;p&gt;Prefer verified / Known Bots fields and stable monitoring IPs. User-Agent matching is a weak secondary signal and is easy to get wrong under Under Attack Mode.&lt;/p&gt;&lt;h3&gt;Is a green score through an origin bypass good enough for clients?&lt;/h3&gt;&lt;p&gt;Only as a labelled diagnostic. Visitors still hit the Cloudflare edge. Report edge-path PageSpeed Insights once security exceptions are correct.&lt;/p&gt;&lt;h3&gt;What if field data looks fine but lab keeps failing?&lt;/h3&gt;&lt;p&gt;That pattern often means real users pass while automation is blocked, or that URL-level field metrics are missing for other reasons. Investigate Cloudflare events first, then reconcile lab versus field with your usual synthetic-versus-RUM rules.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;When Cloudflare blocks PageSpeed Insights automation, the monitoring problem is access, not paint timing. Diagnose the product that challenged the request, apply the narrowest Skip or IP Access that still protects the zone, and re-run the same scheduled path before you rewrite performance budgets. If you want that loop across a multi-tenant portfolio without babysitting every PageSpeed Insights tab, &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/sign-up&quot;&gt;start a free Apogee Watcher trial&lt;/a&gt; or &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/check&quot;&gt;run a quick check&lt;/a&gt; on a priority URL once Cloudflare allows the document through.&lt;/p&gt;&lt;h2 id=&quot;references&quot; class=&quot;scroll-mt-24&quot;&gt;References&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developers.cloudflare.com/bots/get-started/bot-fight-mode/&quot;&gt;Bot Fight Mode&lt;/a&gt; (Cloudflare Docs)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developers.cloudflare.com/bots/troubleshooting/false-positives/&quot;&gt;Handle false positives from Bot Fight Mode or Super Bot Fight Mode&lt;/a&gt; (Cloudflare Docs)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&amp;lt;a href=&quot;https://developers.cloudflare.co
&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27084/cloudflare-and-psi-automation-challenges-allowlists-monitoring</guid>
<pubDate>Fri, 18 Sep 2026 07:04:00 +0000</pubDate>
</item>
<item>
<title>Cache-Control Headers for Web Performance: CDN and Browser Caching That Sticks</title>
<link>https://coderlegion.com/27085/cache-control-headers-for-web-performance-cdn-and-browser-caching-that-sticks</link>
<description>&lt;p&gt;The CDN is enabled. The origin sends sensible browser caching headers. A lab run on the priority URL still shows a slow document Time to First Byte and repeat views that behave like cold loads. In our experience the gap is rarely “caching is off” as a single switch. It is a mismatch between what the origin allows, what the CDN dashboard overrides, and which layer the browser actually reuses on the next navigation. Cache-Control is the contract that ties those layers together. When the contract is vague or copied from a generic template, performance work does not stick past the next platform default or purge.&lt;/p&gt;&lt;p&gt;What follows is a content-type guide for teams who already understand DNS, TLS, and edge routing from &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/network-performance-dns-tls-http-cdn-cache-rules&quot;&gt;Network Performance for Web Teams: DNS, TLS, HTTP, CDN, and Cache Rules&lt;/a&gt;. That piece explains where cache hits change TTFB on the request path. Here we focus on the header strings that make those hits legal, the CDN defaults that undo them, and how to prove the policy held after deploy with scheduled lab runs.&lt;/p&gt;&lt;h2 id=&quot;what-cache-control-changes-on-the-path-to-first-byte-and-repeat-views&quot; class=&quot;scroll-mt-24&quot;&gt;What Cache-Control changes on the path to first byte and repeat views&lt;/h2&gt;&lt;p&gt;The &lt;code&gt;Cache-Control&lt;/code&gt; response header holds directives that tell browsers and shared caches (CDNs, reverse proxies) whether they may store a response, how long it stays fresh, and whether they must revalidate before reuse. Freshness is measured from when the response was generated at the origin, adjusted by any &lt;code&gt;Age&lt;/code&gt; header added by intermediaries. A directive on the HTML document can therefore change first-byte wait on a CDN miss, while directives on CSS, JavaScript, fonts, and images change how much work repeats on the second view without touching the document request at all.&lt;/p&gt;&lt;p&gt;The directives that matter most for web performance work are &lt;code&gt;max-age&lt;/code&gt; (fresh lifetime in seconds), &lt;code&gt;s-maxage&lt;/code&gt; (fresh lifetime for shared caches only), &lt;code&gt;public&lt;/code&gt; and &lt;code&gt;private&lt;/code&gt; (whether a shared cache may store the response), &lt;code&gt;no-cache&lt;/code&gt; (store but revalidate before use), &lt;code&gt;no-store&lt;/code&gt; (do not store), &lt;code&gt;immutable&lt;/code&gt; (while fresh, skip revalidation on reload), &lt;code&gt;must-revalidate&lt;/code&gt;, and extension directives such as &lt;code&gt;stale-while-revalidate&lt;/code&gt; and &lt;code&gt;stale-if-error&lt;/code&gt;. &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control&quot;&gt;MDN documents each directive&lt;/a&gt;; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc9111.html&quot;&gt;RFC 9111&lt;/a&gt; is the normative reference when you need to settle a dispute between engineering and the CDN vendor.&lt;/p&gt;&lt;p&gt;Treat every response as belonging to a content class before you copy a header string from a blog post. HTML entry points, fingerprinted static bundles, unversioned images, authenticated account pages, and public JSON APIs need different policies. The performance win comes from aligning the class, the origin header, the CDN rule, and your purge discipline so they still agree on the live hostname after release.&lt;/p&gt;&lt;h2 id=&quot;browser-cache-versus-cdn-cache-two-layers-that-need-different-directives&quot; class=&quot;scroll-mt-24&quot;&gt;Browser cache versus CDN cache: two layers that need different directives&lt;/h2&gt;&lt;p&gt;Browser caching headers govern the copy on the device. CDN cache rules govern the copy at the edge. They read the same &lt;code&gt;Cache-Control&lt;/code&gt; line but apply different parts of it. &lt;code&gt;s-maxage&lt;/code&gt; applies only to shared caches and overrides &lt;code&gt;max-age&lt;/code&gt; for them when both are present. &lt;code&gt;private&lt;/code&gt; keeps a response out of shared caches while still allowing a browser store. &lt;code&gt;immutable&lt;/code&gt; affects reload behaviour for static assets in the browser. Confusing the two layers produces tickets that say “we purged the CDN but nothing changed,” because the team purged the edge while the browser still held an old document, or vice versa.&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Layer&lt;/th&gt;&lt;th&gt;Who stores&lt;/th&gt;&lt;th&gt;Typical levers&lt;/th&gt;&lt;th&gt;Effect on cold TTFB&lt;/th&gt;&lt;th&gt;Effect on repeat views / LCP&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Browser&lt;/td&gt;&lt;td&gt;Returning visitor&lt;/td&gt;&lt;td&gt;`max-age`, `immutable`, `no-cache` + validators&lt;/td&gt;&lt;td&gt;Small on first visit&lt;/td&gt;&lt;td&gt;Large when static assets skip full downloads&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CDN (shared cache)&lt;/td&gt;&lt;td&gt;Any visitor hitting that PoP&lt;/td&gt;&lt;td&gt;`s-maxage`, `public`, origin cacheability, purge tags&lt;/td&gt;&lt;td&gt;Large on cache hit for HTML and assets&lt;/td&gt;&lt;td&gt;Smaller origin load; stable edge latency&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Origin&lt;/td&gt;&lt;td&gt;Source of truth&lt;/td&gt;&lt;td&gt;App framework defaults, `CDN-Cache-Control` on some stacks&lt;/td&gt;&lt;td&gt;Defines miss cost&lt;/td&gt;&lt;td&gt;N/A until next request&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;A useful policy document names the content class, the origin header, the CDN behaviour when the header is absent, and who purges what on deploy. Without that table, agencies inherit “cache everything” dashboard templates that fight the application’s HTML strategy or strip &lt;code&gt;ETag&lt;/code&gt; headers the framework relies on for cheap &lt;code&gt;304 Not Modified&lt;/code&gt; responses.&lt;/p&gt;&lt;h2 id=&quot;cache-control-for-fingerprinted-css-javascript-fonts-and-images&quot; class=&quot;scroll-mt-24&quot;&gt;Cache-Control for fingerprinted CSS, JavaScript, fonts, and images&lt;/h2&gt;&lt;p&gt;Static assets whose URLs change when content changes should be cached aggressively at both browser and CDN. The filename is the versioning mechanism. A stylesheet served as &lt;code&gt;/assets/app.9f1c2d4e.css&lt;/code&gt; can safely carry a one-year freshness lifetime because the next deploy publishes &lt;code&gt;/assets/app.b3e8a1f0.css&lt;/code&gt; and the HTML entry point references the new name.&lt;/p&gt;&lt;p&gt;Recommended pattern for hashed or fingerprinted files:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Cache-Control: public, max-age=31536000, immutable
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;immutable&lt;/code&gt; directive, defined in &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc8246.html&quot;&gt;RFC 8246&lt;/a&gt;, tells clients not to send conditional revalidation requests while the response is fresh. That matters on hard reloads: without &lt;code&gt;immutable&lt;/code&gt;, browsers may still ask the server whether &lt;code&gt;app.9f1c2d4e.css&lt;/code&gt; changed even though the URL encodes the revision. For performance-sensitive sites, those avoidable round trips show up as extra connection work before Largest Contentful Paint can use a cached font or script.&lt;/p&gt;&lt;p&gt;Do not put &lt;code&gt;immutable&lt;/code&gt; on unversioned paths such as &lt;code&gt;/styles/main.css&lt;/code&gt; or &lt;code&gt;/logo.png&lt;/code&gt; unless your release process never overwrites the same URL. If the URL is stable, use a shorter &lt;code&gt;max-age&lt;/code&gt; plus &lt;code&gt;ETag&lt;/code&gt; or &lt;code&gt;Last-Modified&lt;/code&gt; validators so updates propagate without waiting a year. &lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/articles/uses-long-cache-ttl&quot;&gt;Google’s guidance on efficient cache lifetimes&lt;/a&gt; aligns with the fingerprinted-file rule: long TTLs belong where the URL changes with the bytes.&lt;/p&gt;&lt;p&gt;After you set long TTLs at the origin, confirm the CDN is allowed to store those responses. Some platforms require &lt;code&gt;public&lt;/code&gt; explicitly. Some honour a separate &lt;code&gt;CDN-Cache-Control&lt;/code&gt; or vendor-specific override. Inspect &lt;code&gt;Age&lt;/code&gt;, &lt;code&gt;CF-Cache-Status&lt;/code&gt;, &lt;code&gt;X-Cache&lt;/code&gt;, or the equivalent on a CSS or font request in production, not only on HTML.&lt;/p&gt;&lt;h2 id=&quot;cache-control-for-html-documents-agencies-actually-ship&quot; class=&quot;scroll-mt-24&quot;&gt;Cache-Control for HTML documents agencies actually ship&lt;/h2&gt;&lt;p&gt;HTML is the map to every other asset. If the browser or CDN serves stale HTML, it may point at old bundle names even when the new files already sit on the origin. Most delivery teams therefore want HTML to revalidate often while still allowing the CDN to absorb traffic for anonymous pages.&lt;/p&gt;&lt;p&gt;Common patterns:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Revalidate every use (good default for many marketing sites):&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Cache-Control: no-cache
ETag: &quot;build-20260910-abc123&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;no-cache&lt;/code&gt; does not mean “do not cache.” It means the cache must validate with the origin before reuse. With a matching &lt;code&gt;ETag&lt;/code&gt;, validation is a small &lt;code&gt;304 Not Modified&lt;/code&gt; instead of a full document download. That keeps HTML current without abandoning bandwidth savings.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Short browser freshness, longer edge TTL, background refresh:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=300, stale-if-error=600
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Browsers treat the document as stale immediately and revalidate. The CDN may serve a fresh copy for sixty seconds and continue serving stale responses briefly while it revalidates in the background. This pattern can lower origin load during traffic spikes if your content model tolerates short edge staleness on public pages.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Personalised or authenticated HTML:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Cache-Control: private, no-store
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;or, when the response is user-specific but you still want browser revalidation without CDN storage:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Cache-Control: private, no-cache
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Never mark personalised HTML &lt;code&gt;public&lt;/code&gt; at the CDN unless you have deliberate edge logic that varies the cache key safely. &lt;code&gt;Vary: Cookie&lt;/code&gt; without a tight vary set often collapses hit ratio to zero. Wrong HTML caching creates correctness incidents that outweigh any TTFB improvement.&lt;/p&gt;&lt;p&gt;When you deploy new HTML that references new asset hashes, purge or revalidate HTML at the CDN if your policy allows edge caching at all. Serving cached HTML that still points at removed bundles is a common post-release failure mode that looks like a performance regression in Lighthouse even though the new files are fast.&lt;/p&gt;&lt;h2 id=&quot;cdn-defaults-and-dashboard-rules-that-undo-your-header-policy&quot; class=&quot;scroll-mt-24&quot;&gt;CDN defaults and dashboard rules that undo your header policy&lt;/h2&gt;&lt;p&gt;Agencies often enable a CDN before origin headers are deliberate. Vendors ship attractive defaults: cache all static extensions for a month, “standard” caching for HTML, ignore certain origin headers, or respect only cache rules defined in the dashboard. Those defaults are a starting point, not a performance policy.&lt;/p&gt;&lt;p&gt;Defaults we see cause repeat TTFB and LCP pain:&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Mistake&lt;/th&gt;&lt;th&gt;What breaks&lt;/th&gt;&lt;th&gt;What to check on the live URL&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CDN caches HTML with a long TTL while origin says `no-cache`&lt;/td&gt;&lt;td&gt;Stale pages, wrong asset references after deploy&lt;/td&gt;&lt;td&gt;`CF-Cache-Status` / `X-Cache` on `/` and a template URL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Origin sends `Cache-Control: private` but team expects edge hits&lt;/td&gt;&lt;td&gt;Every request becomes an origin fetch&lt;/td&gt;&lt;td&gt;Response headers on document request&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;“Cache static file types” rule bypasses `immutable` fingerprint policy&lt;/td&gt;&lt;td&gt;Unnecessary revalidation or wrong TTL per extension&lt;/td&gt;&lt;td&gt;Headers on `.js`, `.css`, and font files&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Blanket purge on every CMS save&lt;/td&gt;&lt;td&gt;Hit ratio collapses; TTFB spikes for a day&lt;/td&gt;&lt;td&gt;`Age` header trend across scheduled runs&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;`Vary: Cookie` on anonymous pages&lt;/td&gt;&lt;td&gt;Edge refuses to share entries&lt;/td&gt;&lt;td&gt;Cache key documentation from vendor&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Flexible SSL or redirect loops at edge&lt;/td&gt;&lt;td&gt;TTFB rises before cache policy matters&lt;/td&gt;&lt;td&gt;Redirect count in waterfall (see post 62)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;Dashboard rules should echo origin intent, not fight it. If the application sets &lt;code&gt;CDN-Cache-Control&lt;/code&gt; or vendor-specific surrogate headers, document which header wins when both are present. After any CDN migration, re-audit the same priority URLs you used for the previous vendor. Rules do not transfer verbatim between Cloudflare, Fastly, Akamai, and CloudFront.&lt;/p&gt;&lt;h2 id=&quot;how-cache-control-headers-show-up-in-ttfb-and-lcp-lab-runs&quot; class=&quot;scroll-mt-24&quot;&gt;How Cache-Control headers show up in TTFB and LCP lab runs&lt;/h2&gt;&lt;p&gt;Time to First Byte in &lt;a rel=&quot;nofollow&quot; href=&quot;https://pagespeed.web.dev/&quot;&gt;PageSpeed Insights&lt;/a&gt; and Lighthouse reflects how long the navigation request waited for the first byte of the HTML response. When the CDN serves a fresh document from cache, TTFB often drops sharply compared with an origin miss. When HTML is &lt;code&gt;private, no-store&lt;/code&gt; or bypasses the edge, TTFB still includes origin compute and PoP-to-origin latency even if static assets are well cached.&lt;/p&gt;&lt;p&gt;Largest Contentful Paint inherits that document delay and then adds how quickly the LCP resource itself arrives. A hero image with a long &lt;code&gt;max-age&lt;/code&gt; at the CDN may paint fast on repeat views while the first view still waits on HTML generation. Conversely, perfect HTML caching cannot fix LCP if the LCP image lives at an unversioned URL with a five-minute TTL and the CDN misses every time.&lt;/p&gt;&lt;p&gt;Practical reading order in lab tools:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Document request: TTFB, cache status headers, redirect count.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;LCP element request: cache hit or miss, protocol, priority hints.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Render-blocking CSS and font requests: are they fingerprinted with long TTLs or re-fetching each view?&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Pair a cold run with a repeat view when your tool supports it. Cold exposes DNS, TLS, and first miss behaviour. Repeat exposes whether browser caching headers actually removed duplicate downloads. For operational thresholds on the same URLs, set &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/performance-budget-thresholds-template&quot;&gt;performance budget thresholds&lt;/a&gt; that include TTFB and LCP, not only the Performance score.&lt;/p&gt;&lt;p&gt;Once first byte and paint are acceptable, the LCP work in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/quick-way-to-fix-lcp&quot;&gt;A Quick Way to Fix LCP: Four Changes That Cut Time to Paint&lt;/a&gt; still matters. Cache-Control removes avoidable network round trips; it does not shrink an oversized hero file or late-discovered LCP candidate.&lt;/p&gt;&lt;h2 id=&quot;verify-cache-control-on-live-urls-after-every-deploy&quot; class=&quot;scroll-mt-24&quot;&gt;Verify Cache-Control on live URLs after every deploy&lt;/h2&gt;&lt;p&gt;Header policy is only real on the public hostname users hit. Staging often lacks the CDN overlay or sends different headers from the production origin. After each deploy, cache rule edit, or CMS plugin that touches headers, verify the live response rather than assuming the dashboard screenshot still matches production.&lt;/p&gt;&lt;p&gt;Checklist we use on client sites:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Document URL:&lt;/strong&gt; homepage plus one high-intent template (product, pricing, article).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Representative static asset:&lt;/strong&gt; hashed JS or CSS referenced by that HTML.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;LCP resource:&lt;/strong&gt; image or video request from the same lab run.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cache status:&lt;/strong&gt; &lt;code&gt;Age&lt;/code&gt;, vendor hit/miss header, and the full &lt;code&gt;Cache-Control&lt;/code&gt; line.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validators:&lt;/strong&gt; &lt;code&gt;ETag&lt;/code&gt; or &lt;code&gt;Last-Modified&lt;/code&gt; present where you expect revalidation.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compare to policy doc:&lt;/strong&gt; content class, intended header, CDN rule, purge owner.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Export the header set into the ticket or client report so the next engineer does not re-discover the same miss. A single manual check catches the first mistake. It does not prove the CDN still hits next Thursday after a content editor triggers a purge storm.&lt;/p&gt;&lt;p&gt;Put priority URLs on &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/how-to-schedule-pagespeed-monitoring-test-frequency-priority-portfolio&quot;&gt;scheduled PageSpeed monitoring&lt;/a&gt; with device splits that match the audience. Apogee Watcher stores lab payloads over time so you can see TTFB and LCP drift after edge changes without someone opening PageSpeed Insights from memory. That layers onto your CDN vendor; it does not replace header configuration or purge discipline. For why one-off lab runs are insufficient proof, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/pagespeed-insights-vs-automated-monitoring-when-manual-checks-arent-enough&quot;&gt;PageSpeed Insights vs Automated Monitoring: When Manual Checks Aren't Enough&lt;/a&gt;.&lt;/p&gt;&lt;h2 class=&quot;scroll-mt-24&quot;&gt;FAQ&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;What is the difference between &lt;code&gt;no-cache&lt;/code&gt; and &lt;code&gt;no-store&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;no-cache&lt;/code&gt; allows storage but requires validation before reuse. Browsers and CDNs may keep a copy and often answer with &lt;code&gt;304 Not Modified&lt;/code&gt; when the &lt;code&gt;ETag&lt;/code&gt; still matches. &lt;code&gt;no-store&lt;/code&gt; forbids storing the response anywhere. Use &lt;code&gt;no-cache&lt;/code&gt; for HTML that must stay current but can revalidate cheaply. Use &lt;code&gt;no-store&lt;/code&gt; for sensitive account or payment flows where persistence is unacceptable.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;When should we use &lt;code&gt;immutable&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Use &lt;code&gt;immutable&lt;/code&gt; only on fingerprinted static assets where the URL changes when the bytes change. Pair it with a long &lt;code&gt;max-age&lt;/code&gt;, commonly &lt;code&gt;31536000&lt;/code&gt; seconds. Do not use it on HTML or on unversioned files you overwrite in place.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Does &lt;code&gt;max-age=0&lt;/code&gt; disable caching?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;No. It marks the response as immediately stale, which forces revalidation behaviour similar in practice to &lt;code&gt;no-cache&lt;/code&gt; for many caches, but the directives are not identical in every intermediary. Be explicit: choose &lt;code&gt;no-cache&lt;/code&gt; when you want validation semantics, or &lt;code&gt;no-store&lt;/code&gt; when you want no persistence.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Why did TTFB improve in WebPageTest but not in PageSpeed Insights?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Tools use different locations, cache states, and throttling. A hit from one PoP near the test agent does not prove global behaviour. Align test location with audience geography and inspect cache headers on the document request in both tools before closing the ticket.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Should APIs use the same Cache-Control as HTML?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Public read-heavy JSON can use short browser &lt;code&gt;max-age&lt;/code&gt; with longer &lt;code&gt;s-maxage&lt;/code&gt; and &lt;code&gt;stale-while-revalidate&lt;/code&gt; when responses are identical for all users. Authenticated or user-specific APIs usually need &lt;code&gt;private&lt;/code&gt; or &lt;code&gt;no-store&lt;/code&gt;. Treat each endpoint as its own content class.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;We purged the CDN. Why do clients still see old CSS?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Purging the edge does not clear browser caches. If CSS URLs are not fingerprinted, visitors may keep an old file until &lt;code&gt;max-age&lt;/code&gt; expires. Prefer hashed filenames for bundles and purge HTML if it still references retired names.&lt;/p&gt;&lt;h2 id=&quot;put-header-policy-on-the-same-schedule-as-performance-budgets&quot; class=&quot;scroll-mt-24&quot;&gt;Put header policy on the same schedule as performance budgets&lt;/h2&gt;&lt;p&gt;Pick one priority URL where TTFB or LCP still misses budget. Capture the live &lt;code&gt;Cache-Control&lt;/code&gt; lines on HTML, the LCP asset, and one fingerprinted bundle. Fix the largest layer mismatch first: origin header, CDN rule, or purge process. Re-run lab from a region that matches traffic, then add th
&lt;/p&gt;</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27085/cache-control-headers-for-web-performance-cdn-and-browser-caching-that-sticks</guid>
<pubDate>Thu, 17 Sep 2026 07:04:00 +0000</pubDate>
</item>
<item>
<title>Google Lighthouse Score Explained: How Scoring Works</title>
<link>https://coderlegion.com/27089/google-lighthouse-score-explained-how-scoring-works</link>
<description>&lt;p&gt;A Google Lighthouse score is a lab Performance number from 0 to 100 for one controlled run. Scoring does not average raw milliseconds. Each metric is mapped through an HTTP Archive curve, then weighted into one score for a single URL and one device strategy.&lt;/p&gt;
&lt;p&gt;Agencies usually meet that number as the green or amber circle in PageSpeed Insights. Clients treat it as &quot;Google speed.&quot; It is neither a ranking factor by itself nor a field Core Web Vitals percentile. It is recorded under simulated conditions, then calculated with documented weights that change across Lighthouse majors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Record lab metrics in a throttled run, convert each to a 0–100 metric score, apply Lighthouse 10 weights (TBT 30%, LCP and CLS 25% each, FCP and Speed Index 10% each), and report the blend as lab only. Keep CrUX and Search Console on a separate slide. Chrome’s &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/lighthouse/performance/performance-scoring&quot;&gt;Lighthouse performance scoring&lt;/a&gt; guide is the source of truth; what follows is the agency translation, including a worked example and FAQ.&lt;/p&gt;
&lt;h2 id=&quot;what-a-lighthouse-performance-score-actually-is&quot; class=&quot;scroll-mt-24&quot;&gt;What a Lighthouse Performance score actually is&lt;/h2&gt;
&lt;p&gt;The Performance score is a single 0–100 summary for one lab run of one URL under one device strategy (mobile or desktop). It is not a ranking factor by itself. It is not a rolling average of real visitors. Opportunities and Diagnostics in the report do not add points directly; they explain ways the underlying metrics might improve, which can raise the score indirectly when those metrics move.&lt;/p&gt;
&lt;p&gt;Colour bands in the docs are simple: 0–49 poor (red), 50–89 needs improvement (orange), 90–100 good (green). A perfect 100 is unusually hard. Moving from 99 to 100 can need as much metric improvement as moving from 90 to 94. For client language, “good” means a lab run that cleared 90 under that profile, not that every user on every network had a green experience.&lt;/p&gt;
&lt;p&gt;PageSpeed Insights uses Lighthouse for the lab block. When people say “the PageSpeed Insights score,” they usually mean that lab Performance category, sometimes while looking at a CrUX field panel on the same screen. Keep those two stories separate in every deck.&lt;/p&gt;
&lt;h2 id=&quot;how-lighthouse-records-lab-metrics-in-a-controlled-run&quot; class=&quot;scroll-mt-24&quot;&gt;How Lighthouse records lab metrics in a controlled run&lt;/h2&gt;
&lt;p&gt;Recording comes first. Lighthouse (and PageSpeed Insights lab mode) loads the URL in a controlled environment: a chosen form factor, simulated network and CPU throttling for typical mobile profiles, and a single navigation. During that load it measures timings and layout behaviour. Common Performance metrics include First Contentful Paint (FCP), Speed Index (SI), Largest Contentful Paint (LCP), Total Blocking Time (TBT), and Cumulative Layout Shift (CLS). Diagnostics such as Time to First Byte (TTFB) may appear as supporting detail. None of those values are taken from real visitors in that run.&lt;/p&gt;
&lt;p&gt;The lab profile matters. Mobile and desktop use different throttling and, since Lighthouse 6, different scoring curves so desktop is not scored on mobile HTTP Archive distributions. Running on a quiet office machine with extensions disabled still differs from a busy shared CI worker. Chrome documents common causes of score fluctuation: ads and A/B tests, routing changes, device power, extensions, antivirus. Treat a single paste as a sample from a distribution, not as the only possible score for that URL.&lt;/p&gt;
&lt;p&gt;Scheduled monitoring reduces the “two screenshots, no baseline” problem. You still measure lab conditions, but you compare like with like over time. That is the operational gap we described in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/pagespeed-insights-vs-automated-monitoring-when-manual-checks-arent-enough&quot;&gt;PageSpeed Insights versus automated monitoring&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-lighthouse-calculates-the-0-100-performance-score&quot; class=&quot;scroll-mt-24&quot;&gt;How Lighthouse calculates the 0–100 Performance score&lt;/h2&gt;
&lt;p&gt;Calculation is a second step. Lighthouse does not average raw milliseconds into one number. For each metric it maps the recorded value onto a 0–100 metric score using a log-normal scoring curve calibrated from HTTP Archive site data. Those metric scores stay under the hood in the usual report UI. The visible Performance score is then a weighted average of the metric scores.&lt;/p&gt;
&lt;p&gt;Chrome describes the curve control points clearly. The 25th percentile of HTTP Archive data for a metric maps to a metric score of 50. The 8th percentile maps to 90 (the “good” control point). Between roughly 0.50 and 0.92 the relationship is nearly linear; near 0.96 returns diminish and further metric gains buy less score. Exploring a metric in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://googlechrome.github.io/lighthouse/scorecalc/&quot;&gt;Lighthouse scoring calculator&lt;/a&gt; is the fastest way to see how a raw LCP or TBT value becomes a metric score before weights apply.&lt;/p&gt;
&lt;p&gt;Because the curves come from real-site distributions, “good” is relative to the web as captured in HTTP Archive, not to an arbitrary round number your client invented in a brand guideline. When Lighthouse major versions change weights or curves, historical scores need a footnote. Always cite the Lighthouse version your tool used when you compare quarters.&lt;/p&gt;
&lt;h2 id=&quot;lighthouse-scoring-weights-lighthouse-10&quot; class=&quot;scroll-mt-24&quot;&gt;Lighthouse scoring weights (Lighthouse 10)&lt;/h2&gt;
&lt;p&gt;Chrome’s documented &lt;strong&gt;Lighthouse 10&lt;/strong&gt; weights for the Performance score are:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Weight&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;First Contentful Paint (FCP)&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed Index (SI)&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Largest Contentful Paint (LCP)&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Blocking Time (TBT)&lt;/td&gt;
&lt;td&gt;30%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cumulative Layout Shift (CLS)&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;TBT carries the largest single weight. LCP and CLS share 25% each. FCP and Speed Index are lighter. Earlier majors differed; Lighthouse 8 still weighted Time to Interactive and used a different CLS weight. Verify the table for the version you ship in CI or see in PageSpeed Insights, and re-check the calculator when Google publishes a new major.&lt;/p&gt;
&lt;p&gt;INP may appear in field tooling and in some report contexts, but the lab Performance blend documented for Lighthouse 10 emphasises &lt;strong&gt;TBT&lt;/strong&gt; as the interactivity proxy inside the score. Do not tell a client that “INP is 30% of the Lighthouse score.” Point them at TBT for lab scoring and at field INP for CrUX and Search Console when samples exist. Metric definitions live in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/lcp-inp-cls-what-each-core-web-vital-means-and-how-to-fix-it&quot;&gt;LCP, INP, and CLS explained&lt;/a&gt;; field interactivity detail is in our &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/understanding-inp-newest-core-web-vital&quot;&gt;INP guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;worked-example-from-metric-scores-to-a-performance-number&quot; class=&quot;scroll-mt-24&quot;&gt;Worked example: from metric scores to a Performance number&lt;/h2&gt;
&lt;p&gt;Suppose a mobile lab run produces these &lt;strong&gt;metric scores&lt;/strong&gt; (already mapped through the curves; not raw milliseconds): FCP 90, Speed Index 85, LCP 70, TBT 60, CLS 95. Apply the Lighthouse 10 weights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FCP: 90 × 0.10 = 9.0&lt;/li&gt;
&lt;li&gt;Speed Index: 85 × 0.10 = 8.5&lt;/li&gt;
&lt;li&gt;LCP: 70 × 0.25 = 17.5&lt;/li&gt;
&lt;li&gt;TBT: 60 × 0.30 = 18.0&lt;/li&gt;
&lt;li&gt;CLS: 95 × 0.25 = 23.75&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sum ≈ &lt;strong&gt;76.8&lt;/strong&gt;, which rounds to a Performance score in the mid-70s (needs improvement). The same raw LCP in milliseconds can yield a different metric score when the curve or Lighthouse major changes, so always cite the version beside the number. Open the &lt;a rel=&quot;nofollow&quot; href=&quot;https://googlechrome.github.io/lighthouse/scorecalc/&quot;&gt;Lighthouse scoring calculator&lt;/a&gt; to poke individual levers before you promise a client that &quot;ten points is one easy fix.&quot;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lighthouse version footnote:&lt;/strong&gt; Weights and curves above follow Chrome’s documented &lt;strong&gt;Lighthouse 10&lt;/strong&gt; Performance scoring. PageSpeed Insights and CI runners may ship a different major. When you compare quarters or tools, record the Lighthouse version (or PageSpeed Insights date) on the report. A five-point swing across a major upgrade can be scoring math, not a regression.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;why-the-performance-score-is-not-the-same-as-core-web-vitals-field-data&quot; class=&quot;scroll-mt-24&quot;&gt;Why the Performance score is not the same as Core Web Vitals field data&lt;/h2&gt;
&lt;p&gt;Field Core Web Vitals come from real Chrome users in the Chrome User Experience Report (CrUX), with Search Console and the PageSpeed Insights field section as common surfaces. Lab Performance scores come from one synthetic navigation. Both can mention LCP and CLS; only the lab score folds in Speed Index and TBT with the weights above. Field INP has no twin inside that Lighthouse 10 weight table.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Lab Performance score&lt;/th&gt;
&lt;th&gt;Field Core Web Vitals (CrUX)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Who was measured?&lt;/td&gt;
&lt;td&gt;Controlled lab profile&lt;/td&gt;
&lt;td&gt;Real Chrome users (sample)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What number?&lt;/td&gt;
&lt;td&gt;Weighted 0–100 from metric scores&lt;/td&gt;
&lt;td&gt;Percentiles for LCP, INP, CLS (and related)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interactivity in the blend?&lt;/td&gt;
&lt;td&gt;TBT (lab)&lt;/td&gt;
&lt;td&gt;INP (field)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Good enough for SEO field status?&lt;/td&gt;
&lt;td&gt;No, not by itself&lt;/td&gt;
&lt;td&gt;Search Console / CrUX when eligible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Useful after a deploy?&lt;/td&gt;
&lt;td&gt;Yes, fast regression signal&lt;/td&gt;
&lt;td&gt;Yes, after the rolling window catches up&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Agencies need both clocks. Lab catches “we broke the template today.” Field answers “Chrome users over ~28 days still feel it.” We covered the field lag in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/why-core-web-vitals-fix-not-in-crux-yet-28-day-window&quot;&gt;Why your Core Web Vitals fix is not in CrUX yet&lt;/a&gt; and the synthetic versus RUM split in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/when-to-use-synthetic-vs-real-user-monitoring-performance&quot;&gt;When to use synthetic versus real user monitoring&lt;/a&gt;. Missing field rows are a sampling problem, not proof the lab score is wrong; see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/pagespeed-insights-no-cls-inp-for-url&quot;&gt;When PageSpeed Insights shows no CLS or INP&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;mid-article-check-run-the-url-you-are-arguing-about&quot; class=&quot;scroll-mt-24&quot;&gt;Mid-article check: run the URL you are arguing about&lt;/h2&gt;
&lt;p&gt;Before you debate a screenshot in Slack, &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/check&quot;&gt;run a free PageSpeed check&lt;/a&gt; on the same URL and strategy (mobile or desktop). Pair the lab Performance score with the metric breakdown, then keep the scoring calculator open so stakeholders see how LCP, TBT, and CLS drive the blend. Scheduled history in Apogee Watcher beats one heroic paste after a tag-manager change.&lt;/p&gt;
&lt;h2 id=&quot;why-lighthouse-scores-fluctuate-between-runs&quot; class=&quot;scroll-mt-24&quot;&gt;Why Lighthouse scores fluctuate between runs&lt;/h2&gt;
&lt;p&gt;Variability is normal. The same URL can move several points between back-to-back runs when ads, consent banners, third-party scripts, or CPU contention differ. Chrome’s variability documentation exists for a reason. For retainers, prefer medians or repeated scheduled runs over a single heroic screenshot. Agree the device strategy (mobile versus desktop) before you argue about a five-point swing.&lt;/p&gt;
&lt;p&gt;CI and local DevTools can also disagree because of Lighthouse version, throttling presets, and whether the environment matches PageSpeed Insights. When you automate audits with coding agents or DevTools workflows, still treat the Performance number as lab math under a stated version. Our note on &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/lighthouse-audits-ai-agents-chrome-devtools&quot;&gt;Lighthouse audits with AI agents&lt;/a&gt; covers the agency gap between one-off agent runs and portfolio schedules. Separate audits such as Baseline Features change Best Practices diagnostics without rewriting the Performance weight table; see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/lighthouse-baseline-features-audit&quot;&gt;Lighthouse’s Baseline Features audit&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-agencies-should-report-lighthouse-scores-to-clients&quot; class=&quot;scroll-mt-24&quot;&gt;How agencies should report Lighthouse scores to clients&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Name the artefact:&lt;/strong&gt; “Lighthouse / PageSpeed Insights lab Performance score,” not “Google’s speed ranking.”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;State device and version:&lt;/strong&gt; mobile or desktop, and Lighthouse major when known.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Show the drivers:&lt;/strong&gt; LCP, TBT, and CLS usually explain most of the blend under current weights.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separate field status:&lt;/strong&gt; CrUX / Search Console on their own slide or paragraph.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use trends:&lt;/strong&gt; three or more scheduled runs beat one paste after a deploy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open the calculator&lt;/strong&gt; when a stakeholder wants to know how many milliseconds buy the next ten points.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoid score chasing at 99–100&lt;/strong&gt; unless the contract truly requires vanity greens; spend engineering time on user-visible LCP and TBT first.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Budgets work better on metric thresholds than on a single Performance integer, because the integer hides which metric moved. Pair score trends with LCP and TBT budgets on money URLs so engineering knows what to fix. A five-point score drop that is all TBT is a different ticket from one that is all CLS.&lt;/p&gt;
&lt;h2 id=&quot;where-apogee-watcher-stores-performance-scores&quot; class=&quot;scroll-mt-24&quot;&gt;Where Apogee Watcher stores Performance scores&lt;/h2&gt;
&lt;p&gt;Apogee Watcher schedules PageSpeed Insights tests across multi-tenant portfolios and stores the lab category scores and metric payloads from those runs for trends, budgets, and alerts. That history is lab data under Google’s PSI/Lighthouse pipeline. It is not a substitute for CrUX percentiles, and it does not claim to weight INP inside the Performance blend.&lt;/p&gt;
&lt;p&gt;Use Watcher when you need the same URLs retested on a cadence after deploys, with mobile and desktop strategies and client-ready charts. Use CrUX and Search Console for field Core Web Vitals when samples exist. Use the scoring calculator when someone asks how the 0–100 number is built. Layer the tools; do not collapse them into one green circle in a slide.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/sign-up&quot;&gt;Start a free trial&lt;/a&gt; to schedule PageSpeed tests and keep Performance score trends beside metric budgets. Or &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/check&quot;&gt;run a free PageSpeed check&lt;/a&gt; on a priority URL and open the &lt;a rel=&quot;nofollow&quot; href=&quot;https://googlechrome.github.io/lighthouse/scorecalc/&quot;&gt;Lighthouse scoring calculator&lt;/a&gt; beside the result.&lt;/p&gt;
&lt;h2 class=&quot;scroll-mt-24&quot;&gt;FAQ&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is a Google Lighthouse score?&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;&lt;p&gt;A Google Lighthouse score usually means the Performance category (0–100) from one lab audit, alongside Accessibility, Best Practices, and SEO category scores. The Performance number is the weighted blend covered here. It is not an average of real-user timings and not a Search Console field status.&lt;/p&gt;&lt;br&gt;
&lt;p&gt;&lt;strong&gt;How does Lighthouse scoring work?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Recording comes first: Lighthouse loads the URL under a fixed device and throttling profile and measures lab metrics (FCP, Speed Index, LCP, TBT, CLS under current docs). Calculation comes second: each metric maps to a 0–100 metric score via log-normal HTTP Archive curves, then those metric scores are weighted into the visible Performance number.&lt;/p&gt;&lt;br&gt;
&lt;p&gt;&lt;strong&gt;What is a good Lighthouse Performance score?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Chrome’s colour bands: 0–49 poor, 50–89 needs improvement, 90–100 good. &quot;Good&quot; means that lab run cleared 90 under that profile, not that every visitor on every network had a green experience. Chasing 99–100 is usually vanity; spend engineering time on user-visible LCP and TBT first.&lt;/p&gt;&lt;br&gt;
&lt;p&gt;&lt;strong&gt;How do lab Lighthouse scores differ from PageSpeed Insights and CrUX?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
PageSpeed Insights shows a Lighthouse lab block and, when eligible, a CrUX field block on the same page. The lab circle is Lighthouse scoring. CrUX is aggregated Chrome field data (percentiles for LCP, INP, CLS), not the weighted 0–100 Performance blend. For when to lean on synthetic lab runs versus real-user monitoring, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/when-to-use-synthetic-vs-real-user-monitoring-performance&quot;&gt;when to use synthetic versus real user monitoring&lt;/a&gt;. Do not collapse &quot;the PageSpeed Insights score,&quot; &quot;Lighthouse,&quot; and &quot;Core Web Vitals&quot; into one sentence in a client deck.&lt;/p&gt;&lt;br&gt;
&lt;p&gt;&lt;strong&gt;Does Google use the Lighthouse Performance score as a ranking factor?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Google’s ranking systems use page experience signals tied to real-user Core Web Vitals concepts, not your lab Performance integer as a published ranking weight. Treat the score as an engineering and reporting aid. Use CrUX and Search Console for field status when data exists.&lt;/p&gt;&lt;br&gt;
&lt;p&gt;&lt;strong&gt;Why is Total Blocking Time in the score if INP is the Core Web Vital?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Lab runs need a synthetic interactivity signal during a single load. TBT fills that role inside the documented Lighthouse 10 Performance weights. INP is a field metric built from real interactions over a page life. Report both without merging them into one sentence.&lt;/p&gt;&lt;br&gt;
&lt;p&gt;&lt;strong&gt;Do Opportunities change the Performance score directly?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No. Only metric scores feed the weighted average. Opportunities and Diagnostics guide fixes that may improve those metrics on the next run.&lt;/p&gt;&lt;br&gt;
&lt;p&gt;&lt;strong&gt;Why did desktop scores change a lot after older Lighthouse versions?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Before Lighthouse 6, desktop runs could be scored against mobile-derived curves and look artificially strong. From v6 onward, desktop has its own scoring. Compare desktop history carefully across that boundary.&lt;/p&gt;&lt;br&gt;
&lt;p&gt;&lt;strong&gt;Can Apogee Watcher show how each metric score was weighted for a run?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Watcher stores the PageSpeed Insights / Lighthouse results from scheduled tests for trends and budgets. For interactive weight exploration, use Google’s scoring calculator and Chrome’s scoring documentation alongside your Watcher history.&lt;/p&gt;&lt;br&gt;
&lt;h2 class=&quot;scroll-mt-24&quot;&gt;References&lt;/h2&gt;&lt;br&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/lighthouse/performance/performance-scoring&quot;&gt;Lighthouse performance scoring&lt;/a&gt; (Chrome for Developers)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://googlechrome.github.io/lighthouse/scorecalc/&quot;&gt;Lighthouse scoring calculator&lt;/a&gt; (Chrome)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://httparchive.org/&quot;&gt;HTTP Archive&lt;/a&gt; (scoring curve source data)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/GoogleChrome/lighthouse/blob/main/docs/variability.md&quot;&gt;Lighthouse variability&lt;/a&gt; (Lighthouse project)&lt;/li&gt;
&lt;li&gt;&amp;lt;a href=&quot;https:
&lt;/li&gt;&lt;/ul&gt;</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27089/google-lighthouse-score-explained-how-scoring-works</guid>
<pubDate>Mon, 14 Sep 2026 19:47:44 +0000</pubDate>
</item>
<item>
<title>Late CLS vs Early CLS: How to Diagnose Layout Shift After Load</title>
<link>https://coderlegion.com/27088/late-cls-vs-early-cls-how-to-diagnose-layout-shift-after-load</link>
<description>&lt;p&gt;&lt;strong&gt;Late CLS&lt;/strong&gt; is layout instability that happens after the page has already painted useful content: personalised modules, consent bars that collapse, infinite-scroll batches, client-side route changes, and embeds that activate on scroll. &lt;strong&gt;Early CLS&lt;/strong&gt; is the classic load-time problem: images without dimensions, web fonts that swap metrics, and ad slots that expand when the network returns. Both count toward the same Cumulative Layout Shift score in the field, but they need different fixes and different lab reproduction steps.&lt;/p&gt;&lt;p&gt;If PageSpeed Insights or Lighthouse reports a calm CLS while CrUX or Search Console shows amber or red on the same URL, you are usually looking at &lt;strong&gt;cumulative layout shift after load&lt;/strong&gt;, not a measurement bug. Lab runs emphasise the first few seconds of navigation. Real Chrome users keep scrolling, accept cookies, open menus, and trigger client-rendered updates that never appear in a default synthetic trace. Google’s own CLS guidance calls out that gap when you compare lab tooling with field data (&lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/articles/optimize-cls&quot;&gt;Optimize Cumulative Layout Shift&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;We covered the shared causes and fix order in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/cls-deep-dive-common-causes-fixes-layout-shift&quot;&gt;CLS deep-dive: common causes and fixes for layout shift&lt;/a&gt; and the metric definitions in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/lcp-inp-cls-what-each-core-web-vital-means-and-how-to-fix-it&quot;&gt;LCP, INP, CLS: What Each Core Web Vital Means and How to Fix It&lt;/a&gt;. Below we split early versus late CLS, show how to fix cls after load without chasing load-only optimisations, and tie late shifts to INP when interaction and layout move in the same session.&lt;/p&gt;&lt;h2 id=&quot;early-cls-vs-late-cls-what-each-phase-measures&quot; class=&quot;scroll-mt-24&quot;&gt;Early CLS vs late CLS: what each phase measures&lt;/h2&gt;&lt;p&gt;Cumulative Layout Shift aggregates unexpected movement of visible content during the page lifetime, grouped into session windows so bursts of instability score as one worst cluster rather than endless micro-penalties (&lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/articles/evolving-cls&quot;&gt;Evolving the CLS metric&lt;/a&gt;). “Early” and “late” are operational labels we use on client tickets, not separate Google metrics. They help teams route work to the right owner.&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Phase&lt;/th&gt;&lt;th&gt;Typical timing&lt;/th&gt;&lt;th&gt;Common culprits&lt;/th&gt;&lt;th&gt;First fix lever&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;**Early CLS**&lt;/td&gt;&lt;td&gt;First paint through LCP&lt;/td&gt;&lt;td&gt;Images and video without reserved size, font swap, hero ad slots, SSR/hydration mismatch&lt;/td&gt;&lt;td&gt;Intrinsic dimensions, `aspect-ratio`, font strategy, stable server HTML&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;**Late CLS**&lt;/td&gt;&lt;td&gt;After LCP, often after idle or interaction&lt;/td&gt;&lt;td&gt;Cookie and promo bars, infinite scroll, SPA route transitions, lazy embeds without wrappers, “new since visit” inserts&lt;/td&gt;&lt;td&gt;Reserved slots, overlay UI, route-level skeletons, facade patterns&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;**Interaction-adjacent**&lt;/td&gt;&lt;td&gt;During or right after tap/scroll&lt;/td&gt;&lt;td&gt;Menus, carousels, accordions, chat widgets, heavy click handlers that also insert DOM&lt;/td&gt;&lt;td&gt;Split INP work from layout work; reserve expanded state height&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;Early CLS is what most “how to fix CLS” guides optimise: size media, tame fonts, pin ad boxes. Late CLS is why those guides still leave field scores red. A template can pass lab CLS on a cold load while personalised headers, consent flows, and below-the-fold embeds shift the layout ten seconds later.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.apogeewatcher.com/blog-images/78-psi-lighthouse-cls-diagnostics.png&quot; alt=&quot;PageSpeed Insights lab report for a publisher URL: Lighthouse CLS score with filmstrip frames showing a cookie consent overlay appearing after first paint&quot;&gt;&lt;/p&gt;&lt;h3&gt;Why lab CLS often misses late shifts&lt;/h3&gt;&lt;p&gt;Lighthouse and default PSI runs load the URL once, throttle the network, and finish while the main thread is still settling initial resources. They may not scroll, accept consent, open navigation drawers, or wait for infinite-scroll fetches. Practitioner write-ups on layout shift after page load repeat the same pattern: the metric still applies while content is visible and the user has not intentionally triggered a layout change tied to their gesture in the same interaction window. That does not make lab useless. Lab traces still name unstable nodes during load; you extend lab deliberately when you suspect late CLS with longer recordings, scripted scroll, throttled CPU, and consent granted versus denied states.&lt;/p&gt;&lt;h2 id=&quot;late-cls-causes-we-see-after-lcp&quot; class=&quot;scroll-mt-24&quot;&gt;Late CLS causes we see after LCP&lt;/h2&gt;&lt;h3&gt;Dynamic injection above existing content&lt;/h3&gt;&lt;p&gt;Marketing ribbons, “install the app” bars, and logged-in-only alerts often mount after React or Vue hydrates. If the new node pushes the article column down, field CLS moves even when hero images were sized correctly at first paint. The fix is the same as early CLS for banners, but the failure shows up later: render a placeholder shell in server HTML, use an overlay that does not reflow document flow, or reserve the final banner height before JavaScript runs.&lt;/p&gt;&lt;h3&gt;Client-side routing and layout swaps&lt;/h3&gt;&lt;p&gt;Single-page apps that replace the entire content column on navigation can score layout shifts when the outgoing view unmounts and the incoming view has different chrome height. Users experience this as &lt;strong&gt;cumulative layout shift after load&lt;/strong&gt; on soft navigations. Fix at the route shell: stable header and footer height, skeleton rows that match card dimensions, and avoid prepending “recommended for you” strips above content the user is already reading.&lt;/p&gt;&lt;h3&gt;Infinite scroll and “prepend” feeds&lt;/h3&gt;&lt;p&gt;Append-below infinite scroll is lower risk when card heights are stable. Prepend patterns (“12 new posts since your last visit”) are a frequent late CLS source on publishers and social-style templates. Prefer a dedicated strip with fixed height, or animate within a reserved region instead of reflowing the whole feed.&lt;/p&gt;&lt;h3&gt;Consent, chat, and third-party widgets&lt;/h3&gt;&lt;p&gt;Consent management platforms often inject a tall banner, then collapse it to a small chip. That collapse is a layout shift. Chat widgets slide in from the corner and resize when the first message arrives. Recommendation engines inject carousels beside article bodies after the main content paints. These overlap with the third-party load patterns in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/third-party-scripts-performance-worst-offenders&quot;&gt;Third-Party Scripts and Performance: How to Identify and Fix the Worst Offenders&lt;/a&gt;. Late CLS tickets should name the tag and the clock: load, post-consent, on-scroll, or on-interaction.&lt;/p&gt;&lt;h3&gt;Embeds and video slots that activate late&lt;/h3&gt;&lt;p&gt;YouTube and other iframes are an early CLS problem when the slot has no &lt;code&gt;aspect-ratio&lt;/code&gt;. They become a &lt;strong&gt;late CLS&lt;/strong&gt; problem when the iframe is created only after scroll, consent, or click, and the parent page still had no reserved box at first paint. &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/cross-origin-youtube-embeds-and-cls-what-publishers-can-actually-fix&quot;&gt;Cross-Origin YouTube Embeds and CLS: What Publishers Can Actually Fix&lt;/a&gt; walks through facade and wrapper patterns. The rule for late activation is unchanged: the sized box must exist before the third party arrives, not when the user finally scrolls to it.&lt;/p&gt;&lt;h2 id=&quot;late-cls-and-inp-when-shifts-follow-interaction&quot; class=&quot;scroll-mt-24&quot;&gt;Late CLS and INP: when shifts follow interaction&lt;/h2&gt;&lt;p&gt;Interaction to Next Paint measures responsiveness: how long after input until the browser paints the next frame. Late layout shifts and poor INP often share a root cause: main-thread work and DOM updates triggered by the same tap or scroll.&lt;/p&gt;&lt;p&gt;Examples we log on client sites:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Opening a mega-menu inserts dozens of nodes and shifts the hero underneath.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;A carousel “load more” handler fetches HTML and prepends cards while the user is reading.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;A video facade creates a full iframe on click; if the handler is slow, INP rises; if the box was not reserved, CLS rises in the same session.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Treat INP and CLS as related diagnostics, not competing priorities. DevTools Performance recordings show long tasks and layout events in one timeline. Chrome’s performance insights also surface &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/performance/insights/cls-culprit&quot;&gt;layout shift culprits&lt;/a&gt; alongside interaction delays on supported versions. Fix reserved layout first when the shift is visible; profile the handler when INP alone is red.&lt;/p&gt;&lt;h2 id=&quot;how-to-diagnose-cumulative-layout-shift-after-load&quot; class=&quot;scroll-mt-24&quot;&gt;How to diagnose cumulative layout shift after load&lt;/h2&gt;&lt;p&gt;Work in this order so you do not optimise hero images while a consent bar is the real field problem.&lt;/p&gt;&lt;h3&gt;1. Confirm field pain on the right URL and template&lt;/h3&gt;&lt;p&gt;Use CrUX in PageSpeed Insights, the Core Web Vitals report in Search Console, or your RUM product if the client allows instrumentation. Check whether CLS is origin-wide or concentrated on paths with heavy personalisation, auth, or embeds. Align monitored URLs with templates that actually shift after load, not only the marketing homepage.&lt;/p&gt;&lt;h3&gt;2. Split early vs late in DevTools&lt;/h3&gt;&lt;p&gt;Open Chrome DevTools, enable the Layout Shift Regions overlay, and record a Performance trace longer than a default Lighthouse run. Scroll, open menus, accept or deny consent, and trigger the flows real users take. Note timestamps relative to LCP: shifts in the first two seconds usually point to early CLS; shifts after scroll or interaction point to late CLS. For programmatic signal in staging, the Layout Instability API and &lt;code&gt;PerformanceObserver&lt;/code&gt; can log shift events with impacted nodes and scores (&lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/articles/debug-layout-shifts&quot;&gt;Debug layout shifts&lt;/a&gt;). Chromium-only, but useful for reproducing a scripted journey before you roll a template fix.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.apogeewatcher.com/blog-images/78-chrome-cls-culprit-insights.png&quot; alt=&quot;Chrome for Developers documentation on layout shift culprits: how to pass the insight, common causes such as unsized images and injected iframes, and links to the Performance panel&quot;&gt;&lt;/p&gt;&lt;h3&gt;3. Reproduce in lab with intentional waits&lt;/h3&gt;&lt;p&gt;On PageSpeed Insights or Lighthouse CI, a single cold load is not enough when you suspect &lt;strong&gt;fix cls after load&lt;/strong&gt; work. Supplement with:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Manual PSI runs after scrolling to embed-heavy sections.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;WebPageTest scripts that wait, scroll, and click consent.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Staging builds with CPU throttling and “slow 4G” to stretch the window where late tags arrive.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Compare traces with consent granted and denied. Field CLS is a mix of both populations on many publisher sites.&lt;/p&gt;&lt;h3&gt;4. Attribute shifts to owners&lt;/h3&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Observation&lt;/th&gt;&lt;th&gt;Likely class&lt;/th&gt;&lt;th&gt;Owner&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Shift on first paint before any scroll&lt;/td&gt;&lt;td&gt;Early CLS&lt;/td&gt;&lt;td&gt;Template/CSS, images, fonts&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Shift when banner collapses after accept&lt;/td&gt;&lt;td&gt;Late CLS&lt;/td&gt;&lt;td&gt;CMP + layout shell&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Shift when route changes without full reload&lt;/td&gt;&lt;td&gt;Late CLS&lt;/td&gt;&lt;td&gt;SPA shell / router&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Shift only after tapping play on video&lt;/td&gt;&lt;td&gt;Late CLS + possible INP&lt;/td&gt;&lt;td&gt;Embed wrapper + click handler&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Shift on URLs with no embeds in lab URL list&lt;/td&gt;&lt;td&gt;Journey mix&lt;/td&gt;&lt;td&gt;Monitoring config&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;Label tickets with that table. It stops teams from re-optimising LCP images when the CMP is moving the grid.&lt;/p&gt;&lt;h2 id=&quot;how-to-fix-cls-after-load-without-undoing-early-wins&quot; class=&quot;scroll-mt-24&quot;&gt;How to fix CLS after load without undoing early wins&lt;/h2&gt;&lt;p&gt;You do not need a second metric. You need a second pass on the template after load-time sizing is correct.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reserve space for UI that always appears later.&lt;/strong&gt; Consent bars, sticky promos, and chat launchers get a min-height shell or overlay treatment in the base layout, not a client-only insert into document flow.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stabilise route and feed transitions.&lt;/strong&gt; Match skeleton dimensions to final cards; avoid prepending above the reading position; keep header height consistent across routes.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pair lazy embeds with permanent wrappers.&lt;/strong&gt; Lazy loading without &lt;code&gt;aspect-ratio&lt;/code&gt; is a common way to recreate late jumps. Facades should sit inside the same box the iframe will fill.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Throttle third-party activation.&lt;/strong&gt; Defer non-critical widgets until after first paint &lt;em&gt;into reserved slots&lt;/em&gt;, not into zero-height nodes. Audit tag order with the same discipline as early-load scripts.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add regression checks on journeys, not only home.&lt;/strong&gt; Extend performance budgets beyond the homepage. &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/performance-budget-thresholds-template&quot;&gt;Performance Budget Thresholds Template&lt;/a&gt; includes CLS ceilings per template; add a “post-scroll” or “post-consent” scripted check where late CLS appeared in DevTools.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Scheduled lab monitoring catches CMS and tag-manager drift before CrUX turns red. If you run many client origins, pair template fixes with recurring PSI runs on article, PLP, and checkout URLs, not a single flagship URL.&lt;/p&gt;&lt;h2 id=&quot;faq-late-cls-and-field-monitoring&quot; class=&quot;scroll-mt-24&quot;&gt;FAQ: late CLS and field monitoring&lt;/h2&gt;&lt;h3&gt;Is cumulative layout shift only measured during initial load?&lt;/h3&gt;&lt;p&gt;No. CLS can accumulate across the page lifetime within session windows while content is visible. Late shifts from consent, embeds, and dynamic inserts count the same as early ones. That is why &lt;strong&gt;layout shift after page load&lt;/strong&gt; shows up in field data when lab load-only runs look fine.&lt;/p&gt;&lt;h3&gt;Can I fix late CLS without fixing early CLS first?&lt;/h3&gt;&lt;p&gt;You can parallelise, but early wins are cheaper. Size above-the-fold media and fonts first so traces are readable. If early CLS is still high, late fixes are harder to verify because multiple bursts overlap in one session window.&lt;/p&gt;&lt;h3&gt;Does fixing late CLS help INP?&lt;/h3&gt;&lt;p&gt;Sometimes indirectly. Removing unnecessary DOM insertions and reserving expanded UI reduces main-thread work on interaction. Heavy handlers still need profiling; a CLS patch alone rarely fixes INP on its own.&lt;/p&gt;&lt;h3&gt;Why does Search Console CLS differ from Lighthouse on the same URL?&lt;/h3&gt;&lt;p&gt;Different populations, devices, and journeys. Field data includes navigations where users scrolled, accepted cookies, and hit personalised routes. Lighthouse uses a controlled load. Use lab for element lists; use field for whether the pain is widespread.&lt;/p&gt;&lt;h3&gt;Should agencies report early and late CLS separately?&lt;/h3&gt;&lt;p&gt;Yes, in client language. Stakeholders understand “banner after accept” and “infinite scroll” better than a single CLS number. Splitting the narrative keeps engineering scope honest and speeds approval for CMP or template work.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Late CLS is not a separate Core Web Vital. It is the same metric telling you that stability failed after the load-time checklist was already green. Split early versus late shifts in DevTools, reproduce the journeys field users actually take, and fix slot contracts for UI that arrives after LCP.&lt;/p&gt;&lt;p&gt;When you need scheduled lab coverage across templates and client sites, open &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/pricing&quot;&gt;Apogee Watcher pricing&lt;/a&gt; and pick a plan that matches your portfolio size.&lt;/p&gt;&lt;h2 class=&quot;scroll-mt-24&quot;&gt;References&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/articles/optimize-cls&quot;&gt;Optimize Cumulative Layout Shift&lt;/a&gt; (web.dev)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/articles/cls&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; (web.dev)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/articles/evolving-cls&quot;&gt;Evolving the CLS metric&lt;/a&gt; (web.dev)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/articles/debug-layout-shifts&quot;&gt;Debug layout shifts&lt;/a&gt; (web.dev)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/performance/insights/cls-culprit&quot;&gt;Layout shift culprits&lt;/a&gt; (Chrome for Developers)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/cls-deep-dive-common-causes-fixes-layout-shift&quot;&gt;CLS deep-dive: common causes and fixes for layout shift&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/lcp-inp-cls-what-each-core-web-vital-means-and-how-to-fix-it&quot;&gt;LCP, INP, CLS: What Each Core Web Vital Means and How to Fix It&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/cross-origin-youtube-embeds-and-cls-what-publishers-can-actually-fix&quot;&gt;Cross-Origin YouTube Embeds and CLS: What Publishers Can Actually Fix&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/third-party-scripts-performance-worst-offenders&quot;&gt;Third-Party Scripts and Performance: How to Identify and Fix the Worst Offenders&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/performance-budget-thresholds-template&quot;&gt;Performance Budget Thresholds Template&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27088/late-cls-vs-early-cls-how-to-diagnose-layout-shift-after-load</guid>
<pubDate>Mon, 14 Sep 2026 08:04:00 +0000</pubDate>
</item>
<item>
<title>Why Your Store's Conversion Problem Is Usually In The Code</title>
<link>https://coderlegion.com/24871/why-your-stores-conversion-problem-is-usually-in-the-code</link>
<description>&lt;p&gt;Most conversion advice that reaches developers is written for marketers, so it stops at &quot;add trust signals&quot; and never names a file to open. This is the version for the people who actually own the checkout template.&lt;/p&gt;
&lt;h3 id=&quot;the-number-that-sets-the-stakes&quot;&gt;The Number That Sets The Stakes&lt;/h3&gt;
&lt;p&gt;The average ecommerce store converts somewhere between 2 and 3 percent, which means 97 or 98 of every 100 visitors leave without buying. The instinct is to go buy more traffic. The cheaper move is to find where the existing traffic is dropping out, because that work compounds and ad spend does not.&lt;/p&gt;
&lt;h3 id=&quot;checkout-is-an-engineering-problem&quot;&gt;Checkout Is An Engineering Problem&lt;/h3&gt;
&lt;p&gt;Almost every checkout has friction that exists because a field was easier to add than to remove. Forced account creation before purchase. Nine fields where five would do. Address validation that rejects a legitimate apartment format. Payment methods the buyer does not already have set up.&lt;/p&gt;
&lt;p&gt;The single most common abandonment reason is still shipping cost appearing at the final step. Nothing about moving that number earlier is hard, it is a template change and a shipping estimate call, and it consistently outperforms most redesigns.&lt;/p&gt;
&lt;h3 id=&quot;speed-is-a-conversion-feature&quot;&gt;Speed Is A Conversion Feature&lt;/h3&gt;
&lt;p&gt;Mobile conversion falls off sharply between two and four seconds. Most stores are carrying a few hundred kilobytes of third party apps, pixels and tag manager containers that nobody has audited in a year, each one added for a reason that expired.&lt;/p&gt;
&lt;p&gt;Load your own product page with a throttled connection and a cold cache, then open the network tab and sort by transfer size. The list of things you can delete without anyone noticing is usually longer than expected.&lt;/p&gt;
&lt;h3 id=&quot;instrument-before-you-optimize&quot;&gt;Instrument Before You Optimize&lt;/h3&gt;
&lt;p&gt;You cannot fix a funnel you cannot see. Per step drop off between product view, add to cart, checkout start, shipping, payment and purchase is the minimum. Without it, every conversation about conversion is an argument between opinions.&lt;/p&gt;
&lt;p&gt;Segment it too. A store at 2 percent overall is often at 4 percent on branded search and under 1 percent on cold paid traffic, and those two numbers need completely different fixes.&lt;/p&gt;
&lt;h3 id=&quot;tests-that-actually-resolve&quot;&gt;Tests That Actually Resolve&lt;/h3&gt;
&lt;p&gt;Here is the part that trips up engineering teams new to CRO. On a 2 percent baseline, detecting a realistic relative lift needs far more sessions per variant than most stores get in a two week window. Run the sample size math before the test, not after.&lt;/p&gt;
&lt;p&gt;If the traffic is not there, ship the changes that are correct on their own merits, removing a broken validation rule does not need an experiment, and watch the trend instead of pretending a fourteen day test settled anything.&lt;/p&gt;
&lt;h3 id=&quot;where-to-go-deeper&quot;&gt;Where To Go Deeper&lt;/h3&gt;
&lt;p&gt;The page level checklists, the testing framework and the measurement setup are all laid out in this &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.afcommerce.com/conversion-optimization/&quot;&gt;complete guide to ecommerce conversion rate optimization&lt;/a&gt;, which is the long form version of everything above.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24871/why-your-stores-conversion-problem-is-usually-in-the-code</guid>
<pubDate>Tue, 18 Aug 2026 22:55:14 +0000</pubDate>
</item>
<item>
<title>When PageSpeed Insights Shows No CLS or INP for Your URL</title>
<link>https://coderlegion.com/24401/when-pagespeed-insights-shows-no-cls-or-inp-for-your-url</link>
<description>&lt;p&gt;You paste a client URL into PageSpeed Insights and the lab block looks fine: Largest Contentful Paint, Total Blocking Time, even a Cumulative Layout Shift score from Lighthouse. Scroll to the field section and Interaction to Next Paint (INP) or CLS is simply absent. Not red, not amber, just missing. The account manager asks whether the page fails Core Web Vitals. You are not looking at a broken test. You are looking at a Chrome User Experience Report (CrUX) eligibility gap, and it is one of the most common sources of confusion in agency reporting.&lt;/p&gt;&lt;p&gt;PageSpeed Insights merges two data sources on one screen. Lab numbers are synthetic: Lighthouse runs on demand with a fixed device profile. Field numbers come from CrUX, a 28-day rolling sample of real Chrome users. Google only publishes a field metric when enough opted-in sessions meet privacy and quality thresholds for that origin, URL, device class, and metric. When the sample is too thin, PSI shows no data for that metric even though lab CLS or INP is right there. That split is normal on low-traffic pages, new launches, long-tail templates, and some desktop-only views. The fix is not to rerun the test until a number appears. The fix is to know which layer you are reading, follow a short decision tree, and fall back to scheduled lab monitoring where field data cannot exist yet.&lt;/p&gt;&lt;h2 id=&quot;why-pagespeed-insights-sometimes-shows-no-cls-or-inp-field-data&quot; class=&quot;scroll-mt-24&quot;&gt;Why PageSpeed Insights sometimes shows no CLS or INP field data&lt;/h2&gt;&lt;p&gt;CrUX is not a census of every visit. Google aggregates performance from a subset of Chrome users who have opted in to sharing statistics. Before a metric appears in PageSpeed Insights field data, the URL or origin needs enough recent sessions in that collection period. If the count falls below Google's threshold, the metric is withheld rather than shown with misleading precision.&lt;/p&gt;&lt;p&gt;Several situations trigger a missing field row:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Insufficient sample size&lt;/strong&gt; for the specific URL, especially on blog posts, campaign landers, and localised paths that get far fewer sessions than the homepage.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Origin-level data only&lt;/strong&gt;, where CrUX has enough traffic for &lt;code&gt;example.com&lt;/code&gt; but not for &lt;code&gt;example.com/pricing/enterprise&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Metric-specific gaps&lt;/strong&gt;, where LCP field data exists but CLS or INP does not, because each metric has its own eligibility within the same report.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Form factor splits&lt;/strong&gt;, where mobile field INP is published but desktop INP is not, or the reverse, because phone and desktop traffic are reported separately.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;New or recently changed URLs&lt;/strong&gt; that have not yet accumulated four weeks of blended field history in the rolling window described in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/why-core-web-vitals-fix-not-in-crux-yet-28-day-window&quot;&gt;why your Core Web Vitals fix is not in CrUX yet&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;None of these mean the page is fast or slow in the eyes of Google Search. They mean Google does not have enough field evidence to print a percentile for that slice. Search Console may still group the URL with origin-level status when URL-level field data is missing, which is another reason teams misread a single PageSpeed Insights screenshot as the full story.&lt;/p&gt;&lt;h2 id=&quot;lab-cls-and-inp-vs-missing-crux-field-data-on-the-same-url&quot; class=&quot;scroll-mt-24&quot;&gt;Lab CLS and INP vs missing CrUX field data on the same URL&lt;/h2&gt;&lt;p&gt;The most confusing PSI screen is lab green beside an empty field row. Lighthouse always calculates lab CLS from the layout shifts it observes during the synthetic load. Lab interactivity is proxied through Total Blocking Time and related audits; INP itself is a field metric, so PSI does not show a field INP value when CrUX has no sample, even if lab TBT looks acceptable.&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Lab (Lighthouse in PageSpeed Insights)&lt;/th&gt;&lt;th&gt;Field (CrUX in PageSpeed Insights)&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Source&lt;/td&gt;&lt;td&gt;One controlled run you triggered&lt;/td&gt;&lt;td&gt;Opted-in Chrome users, 28-day window&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CLS&lt;/td&gt;&lt;td&gt;Always calculated for that run&lt;/td&gt;&lt;td&gt;Shown only when CrUX sample is large enough&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;INP&lt;/td&gt;&lt;td&gt;Not a lab Core Web Vital; use TBT as a lab signal&lt;/td&gt;&lt;td&gt;Shown only when enough real interactions were captured&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Best for&lt;/td&gt;&lt;td&gt;Template QA, deploy checks, before/after on priority URLs&lt;/td&gt;&lt;td&gt;SEO and client field status when eligible&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Missing data meaning&lt;/td&gt;&lt;td&gt;N/A (rerun always produces lab output)&lt;/td&gt;&lt;td&gt;Usually low traffic or URL-level ineligibility, not a PSI bug&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;Treat the two blocks as complementary. Lab answers whether today's build looks clean on a representative device profile. Field answers whether enough real users experienced that quality over the past month. A missing field CLS or INP row does not invalidate lab work; it limits what you can claim about population-level Core Web Vitals for that exact URL. For the wider lab versus field programme, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/when-to-use-synthetic-vs-real-user-monitoring-performance&quot;&gt;synthetic versus real user monitoring&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/pagespeed-insights-vs-automated-monitoring-when-manual-checks-arent-enough&quot;&gt;PageSpeed Insights versus automated monitoring&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;when-crux-shows-origin-level-data-but-not-url-level-cls-or-inp&quot; class=&quot;scroll-mt-24&quot;&gt;When CrUX shows origin-level data but not URL-level CLS or INP&lt;/h2&gt;&lt;p&gt;CrUX publishes at two granularities when sample size allows: &lt;strong&gt;origin&lt;/strong&gt; (scheme plus registrable domain, for example &lt;code&gt;https://www.example.com&lt;/code&gt;) and &lt;strong&gt;URL&lt;/strong&gt; (the exact page tested). High-traffic homepages and top category routes often qualify for URL-level field data. Long-tail URLs frequently inherit only origin-level aggregates in PageSpeed Insights, or show no URL row at all.&lt;/p&gt;&lt;p&gt;If origin field CLS looks healthy but the product detail URL you care about has no field INP, you are not necessarily looking at a product-page regression. You may be looking at a page that does not meet URL-level thresholds. Google's &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/crux/guides/pagespeed-insights&quot;&gt;PageSpeed Insights and CrUX guide&lt;/a&gt; documents how origin and URL slices appear in the UI. Practical workflow for agencies:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Run PageSpeed Insights on the &lt;strong&gt;origin&lt;/strong&gt; (homepage or canonical host) and note which metrics appear at origin level.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Run the &lt;strong&gt;specific client URL&lt;/strong&gt; and compare which metrics disappear.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;If origin field data exists but URL data does not, report origin status for SEO conversations and use lab monitoring on the priority template until URL-level field history builds.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Query the CrUX History API or CrUX Vis for origin trends when you need charts; see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/crux-dashboard-retired-where-to-get-ttfb-inp-field-history&quot;&gt;CrUX Dashboard retired: where to get TTFB, INP, and field history&lt;/a&gt; for the replacement stack.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;URL-level CrUX is valuable when you have it. It is not guaranteed for every page in the sitemap, and missing URL-level CLS or INP is often a traffic maths problem rather than a measurement error.&lt;/p&gt;&lt;h2 id=&quot;desktop-vs-mobile-splits-when-only-one-form-factor-has-field-data&quot; class=&quot;scroll-mt-24&quot;&gt;Desktop vs mobile splits when only one form factor has field data&lt;/h2&gt;&lt;p&gt;PageSpeed Insights field data is split by &lt;strong&gt;phone&lt;/strong&gt; and &lt;strong&gt;desktop&lt;/strong&gt; form factors. Each combination of origin or URL, metric, and form factor has its own eligibility. That is why community threads describe CLS on desktop in one tool and no CLS on mobile in PageSpeed Insights, or INP on mobile with no desktop INP row.&lt;/p&gt;&lt;p&gt;Mobile traffic usually dominates retail and publisher sites, so mobile field Core Web Vitals are what Search Console emphasises for many properties. B2B sites with heavy desktop usage during business hours can still lack desktop INP in CrUX while mobile INP passes. Always note which strategy you selected in PageSpeed Insights before screenshotting a report. Switching between mobile and desktop lab profiles does not change field eligibility, but it changes which field column you are looking at.&lt;/p&gt;&lt;p&gt;For portfolio programmes, monitor both form factors on priority templates even when only one has field data today. &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/mobile-vs-desktop-core-web-vitals-monitoring-both&quot;&gt;Mobile versus desktop Core Web Vitals monitoring&lt;/a&gt; covers schedules and budgets; the reporting rule here is simple: never merge mobile and desktop field percentiles into a single averaged number for client decks.&lt;/p&gt;&lt;h2 id=&quot;decision-tree-what-to-check-when-pagespeed-insights-has-no-data&quot; class=&quot;scroll-mt-24&quot;&gt;Decision tree: what to check when PageSpeed Insights has no data&lt;/h2&gt;&lt;p&gt;Use this sequence before you tell a client their CLS or INP &quot;does not exist.&quot;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;1. Confirm the exact URL (scheme, host, trailing slash) matches the live page
        |
        v
2. Check ORIGIN field data in PageSpeed Insights (same host, homepage if needed)
        |
        +-- Origin also missing --&amp;gt; likely low traffic or new site; skip to step 5
        |
        v
3. Check URL-level field data for the same metric on MOBILE and DESKTOP
        |
        +-- One form factor missing --&amp;gt; report the eligible one; lab-monitor the other
        |
        v
4. Compare LCP vs CLS vs INP availability (metric-specific gaps are common)
        |
        +-- Only INP/CLS missing --&amp;gt; interactions or shifts rare vs LCP paint; still fix in lab
        |
        v
5. Run lab Lighthouse on the template; store scheduled lab trends for proof
        |
        v
6. Set performance budgets and alerts on lab CLS/TBT (and field when it appears)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Step 4 matters for triage. A URL with field LCP but no field CLS often still has layout shift risk; Lighthouse lab CLS on that template is your honest diagnostic until CrUX accumulates enough sessions. A URL with no field INP may still have interaction pain visible as lab TBT or as INP in Chrome DevTools performance recordings on a real device.&lt;/p&gt;&lt;p&gt;If origin and URL both lack all field metrics, read Google's public guidance on &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/crux&quot;&gt;discovering what your real users experience&lt;/a&gt; and treat the property as &lt;strong&gt;field-ineligible&lt;/strong&gt; for now. SEO impact is evaluated at the origin level where data exists; product impact on a thin URL still warrants lab budgets because your users feel the page even when CrUX does not publish a percentile.&lt;/p&gt;&lt;h2 id=&quot;how-agencies-should-monitor-low-traffic-urls-without-crux-samples&quot; class=&quot;scroll-mt-24&quot;&gt;How agencies should monitor low-traffic URLs without CrUX samples&lt;/h2&gt;&lt;p&gt;When PageSpeed Insights returns no field CLS or INP, your operating model shifts from field percentiles to &lt;strong&gt;template-level lab cadence&lt;/strong&gt; plus origin field context.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Pick representative URLs, not every long-tail path.&lt;/strong&gt; Monitor checkout, pricing, primary landing templates, and top blog layouts on a schedule. One good template lab series beats fifty one-off PSI pastes that all show &quot;no data.&quot;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Store lab CLS and TBT (INP proxy) over time.&lt;/strong&gt; Field INP may appear later if traffic grows; until then, regression alerts on lab metrics catch script and font changes that would hurt real users. Tie thresholds to &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/performance-budget-thresholds-template&quot;&gt;performance budget templates&lt;/a&gt; and explain in client reports that field slices are pending, not ignored.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Use origin field data for executive summaries.&lt;/strong&gt; When URL-level INP is missing, origin mobile INP still informs whether the domain broadly passes interactivity expectations. Pair that with lab evidence on the specific template you shipped last Tuesday.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Document device splits in every report.&lt;/strong&gt; Note &quot;mobile field INP: 142 ms p75; desktop field INP: not available (insufficient CrUX sample)&quot; so stakeholders do not assume desktop is fine because mobile is green.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Re-test after traffic events.&lt;/strong&gt; Product launches, paid campaigns, and seasonal enrolment can push a URL over CrUX URL thresholds. Schedule a quarterly eligibility check on pages that matter commercially.&lt;/p&gt;&lt;p&gt;Apogee Watcher is built for that layered model: scheduled PageSpeed Insights and Lighthouse runs across many client sites, budgets on lab metrics, and alerts when a priority URL regresses, while you still read field slices where Google publishes them. It does not manufacture CrUX samples for pages Chrome users rarely visit. It keeps lab proof continuous until field data catches up.&lt;/p&gt;&lt;p&gt;When field CLS or INP returns, compare it to the lab trend you stored. If they diverge, you are usually looking at cache mix, device diversity, or third-party behaviour that lab did not emulate. That is when &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/cls-deep-dive-common-causes-fixes-layout-shift&quot;&gt;CLS deep-dive fixes&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/lcp-inp-cls-what-each-core-web-vital-means-and-how-to-fix-it&quot;&gt;LCP, INP, and CLS explained&lt;/a&gt; move from reference reading to sprint work.&lt;/p&gt;&lt;h2 class=&quot;scroll-mt-24&quot;&gt;FAQ&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Why does PageSpeed Insights show lab CLS but no field CLS?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Lab CLS comes from Lighthouse on every run. Field CLS requires enough real Chrome sessions on that origin or URL in CrUX. Low traffic, URL-level ineligibility, or a form factor with too few sessions are the usual causes.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Does missing INP in PageSpeed Insights mean the page passes interactivity?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;No. It means CrUX does not have enough interaction data to publish field INP for that slice. Use lab TBT, manual device testing, and scheduled lab monitoring until field INP appears or you confirm the URL is intentionally low priority.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Can I force URL-level CrUX data to appear?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;You cannot opt in a single URL. Eligibility follows Chrome traffic volume and Google's privacy thresholds. Growing organic and paid traffic to the URL, fixing canonical and redirect issues, and waiting through the 28-day rolling window are the levers you actually have.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Should I report origin data when the client asked about one URL?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Yes, with a clear label. Origin field Core Web Vitals describe the domain's eligible traffic pool. Pair origin field status with lab results on the specific template so the client sees both the SEO-wide picture and the page they shipped.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Is &quot;no data&quot; the same as a failing Core Web Vital?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;No. Missing field data is not a pass or fail badge. Search Console may use origin-level grouping for URLs without their own field row. Treat missing data as &quot;not enough field evidence,&quot; then use lab monitoring and origin context until URL-level metrics qualify.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;When should I use the CrUX API instead of PageSpeed Insights?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Use PageSpeed Insights for a quick human-readable check. Use the &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/crux/api&quot;&gt;CrUX API&lt;/a&gt; or History API when you need to store daily or weekly field percentiles in your own database across many origins. Eligibility rules are the same; the API does not bypass sample limits.&lt;/p&gt;&lt;p&gt;Missing CLS or INP in PageSpeed Insights is a data availability problem, not a mystery bug. Read origin before URL, mobile before you assume desktop, lab before you declare victory, and keep scheduled synthetic monitoring on the templates that drive revenue while CrUX catches up.&lt;/p&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/sign-up&quot;&gt;Start a free trial&lt;/a&gt; to schedule PageSpeed tests, set lab budgets on priority URLs, and track regressions across client portfolios when field data is thin or absent.&lt;/p&gt;&lt;h2 class=&quot;scroll-mt-24&quot;&gt;References&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/crux/guides/pagespeed-insights&quot;&gt;How to view Chrome UX Report data on PageSpeed Insights&lt;/a&gt; (Chrome for Developers)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/crux&quot;&gt;Chrome User Experience Report documentation&lt;/a&gt; (Chrome for Developers)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/crux/api&quot;&gt;CrUX API&lt;/a&gt; (Chrome for Developers)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/what-are-core-web-vitals-a-practical-guide-for-2026&quot;&gt;What Are Core Web Vitals? A Practical Guide for 2026&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/lcp-inp-cls-what-each-core-web-vital-means-and-how-to-fix-it&quot;&gt;LCP, INP, CLS: What Each Core Web Vital Means and How to Fix It&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/cls-deep-dive-common-causes-fixes-layout-shift&quot;&gt;CLS Deep-Dive: Common Causes and Fixes for Layout Shift&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/pagespeed-insights-vs-automated-monitoring-when-manual-checks-arent-enough&quot;&gt;PageSpeed Insights vs Automated Monitoring: When Manual Checks Aren't Enough&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/when-to-use-synthetic-vs-real-user-monitoring-performance&quot;&gt;When to Use Synthetic vs Real User Monitoring for Performance&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/crux-dashboard-retired-where-to-get-ttfb-inp-field-history&quot;&gt;CrUX Dashboard Retired: Where to Get TTFB, INP, and Field History&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/why-core-web-vitals-fix-not-in-crux-yet-28-day-window&quot;&gt;Why Your Core Web Vitals Fix Isn't in CrUX Yet (28-Day Window)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/mobile-vs-desktop-core-web-vitals-monitoring-both&quot;&gt;Mobile vs Desktop Core Web Vitals: Why You Need to Monitor Both&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/performance-budget-thresholds-template&quot;&gt;Performance Budget Thresholds Template&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24401/when-pagespeed-insights-shows-no-cls-or-inp-for-your-url</guid>
<pubDate>Tue, 11 Aug 2026 10:52:00 +0000</pubDate>
</item>
<item>
<title>CrUX Dashboard Retired: Where to Get TTFB, INP, and Field History</title>
<link>https://coderlegion.com/24402/crux-dashboard-retired-where-to-get-ttfb-inp-and-field-history</link>
<description>&lt;p&gt;For years the CrUX Dashboard was the bookmark agencies opened when a client asked whether field Largest Contentful Paint had moved since the last deploy. It was a Looker Studio report on monthly Chrome User Experience Report (CrUX) data: stacked bars for origin-level distributions, a familiar place to screenshot TTFB, Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS) without writing SQL. Google retired it at the end of November 2025 and shut down the shared CrUX Connector that fed it. If your team still has a saved link, the chart either fails to load or stops at the last month the connector received.&lt;/p&gt;&lt;p&gt;The Chrome UX Report itself did not go away. What you need now is a replacement stack: CrUX Vis and the CrUX History API for TTFB, INP, and CLS trends, PageSpeed Insights and Search Console for the latest field slice, BigQuery if you want years of origin history on your own billing project, and scheduled lab monitoring so deploy proof does not wait on CrUX's 28-day rolling window. No single free dashboard copies every old Looker view, but the migration path is straightforward once you match each tool to the question you are actually asking.&lt;/p&gt;&lt;h2 id=&quot;why-google-retired-the-crux-dashboard-and-looker-studio-connector&quot; class=&quot;scroll-mt-24&quot;&gt;Why Google retired the CrUX Dashboard and Looker Studio connector&lt;/h2&gt;&lt;p&gt;Google announced the deprecation in &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/blog/crux-dashboard-deprecation&quot;&gt;September 2025&lt;/a&gt;. The CrUX Dashboard was a Chrome team-maintained Looker Studio template on monthly CrUX tables. Usage grew after Core Web Vitals became a ranking signal in 2020, but the connector behind it struggled with outages, especially around monthly data releases. Google pointed teams at the &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/crux/guides/history-api&quot;&gt;CrUX History API&lt;/a&gt; (weekly series, up to about 40 points) and &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/crux/vis&quot;&gt;CrUX Vis&lt;/a&gt; (first-party charts on that API) as more reliable paths.&lt;/p&gt;&lt;p&gt;By November 2025 the connector stopped receiving new months. Dashboards that depended on Google's shared BigQuery bridge stopped updating; &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/crux/release-notes&quot;&gt;CrUX release notes&lt;/a&gt; treated that as the final reminder. You can still run the old Looker template against your own &lt;code&gt;chrome-ux-report&lt;/code&gt; BigQuery project if you accept query cost and setup work, but the free one-click version most teams used is not coming back. CrUX Vis now carries much of the same job with weekly points instead of monthly bars, and PageSpeed Insights still shows the latest rolling 28-day field slice when Google has enough Chrome traffic for the origin or URL.&lt;/p&gt;&lt;h2 id=&quot;pagespeed-insights-field-data-and-search-console-current-chrome-ux-report-numbers&quot; class=&quot;scroll-mt-24&quot;&gt;PageSpeed Insights field data and Search Console: current Chrome UX Report numbers&lt;/h2&gt;&lt;p&gt;When someone asks &quot;what does CrUX say right now?&quot;, they usually want the latest rolling field percentiles, not six months of history. Three surfaces answer that question most often, and they share the same underlying dataset with different filters and presentation. PageSpeed Insights, Search Console, and the daily CrUX API each emphasise a different workflow, but the numbers trace back to the same Chrome User Experience Report collection rules.&lt;/p&gt;&lt;h3&gt;PageSpeed Insights field data for one origin or URL&lt;/h3&gt;&lt;p&gt;PageSpeed Insights is still the default check for a single origin or URL. Open &lt;a rel=&quot;nofollow&quot; href=&quot;https://pagespeed.web.dev/&quot;&gt;PageSpeed Insights&lt;/a&gt;, run the test, and read the field section when CrUX has eligible traffic. You get origin-level and, when sample size allows, URL-level p75 values for Core Web Vitals and supporting metrics, including experimental field Time to First Byte (TTFB) where Google publishes it. Lab Lighthouse numbers on the same screen are synthetic; field numbers are the 28-day rolling average described in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/why-core-web-vitals-fix-not-in-crux-yet-28-day-window&quot;&gt;why your Core Web Vitals fix is not in CrUX yet&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;PageSpeed Insights lab data versus field data on the same URL&lt;/h3&gt;&lt;p&gt;PageSpeed Insights shows lab and field on one page, which is convenient and easy to misread. Lab data is a controlled Lighthouse run on demand: fixed device profile, emulated network, one load you triggered. Field data is CrUX: real Chrome users over roughly the past 28 days, blended across devices, cache states, and journeys. A green lab Performance score beside an amber field INP band is normal after a deploy, on a low-traffic URL, or when third-party scripts behave differently for real users than in the lab.&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;&lt;/p&gt;&lt;/th&gt;&lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Lab (Lighthouse in PageSpeed Insights)&lt;/p&gt;&lt;/th&gt;&lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Field (CrUX in PageSpeed Insights)&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Source&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Synthetic test you run&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Opted-in Chrome sessions&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Clock&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Same day, every run&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;28-day rolling average&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Best for&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Deploy verification, before/after on priority URLs&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;SEO reporting, client field status&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Replaces CrUX Dashboard history?&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;No (point-in-time unless you store runs)&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;No (latest period only; use CrUX Vis or History API for trends)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;Treat the two blocks as complementary, not competing scores. Lab answers whether the change shipped cleanly; field answers whether enough real users have lived with it for the aggregate to move. For a fuller split between synthetic schedules and real-user monitoring, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/when-to-use-synthetic-vs-real-user-monitoring-performance&quot;&gt;synthetic versus real user monitoring&lt;/a&gt;. The CrUX Dashboard retirement does not remove that distinction; it removes one place that charted field history without opening PageSpeed Insights, CrUX Vis, or an API client.&lt;/p&gt;&lt;h3&gt;Search Console Core Web Vitals report for SEO workflows&lt;/h3&gt;&lt;p&gt;Search Console's Core Web Vitals report groups URLs by status (Good, Needs improvement, Poor) at origin granularity for SEO workflows. It is slower to navigate than PageSpeed Insights for one URL, but account managers already live there for indexing and query data. Field metrics inherit the same CrUX eligibility rules: low-traffic pages may show origin-only data or gaps, which we will unpack further in a later post on missing CLS and INP in PageSpeed Insights.&lt;/p&gt;&lt;h3&gt;Daily CrUX API for the latest field collection period&lt;/h3&gt;&lt;p&gt;The daily CrUX API (&lt;code&gt;queryRecord&lt;/code&gt; endpoint) returns the same latest collection period programmatically. Use it when you want to store today's field p75 beside yesterday's in your own database, or when a script needs TTFB, INP, or CLS without opening a browser. It refreshes roughly daily with a short processing lag; it does not return the long weekly history that the History API exposes.&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Need&lt;/p&gt;&lt;/th&gt;&lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Tool&lt;/p&gt;&lt;/th&gt;&lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Granularity&lt;/p&gt;&lt;/th&gt;&lt;th rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;History&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Quick check on one URL&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;PageSpeed Insights&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Origin + URL when eligible&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Latest 28-day period only&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;SEO reporting by URL group&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Search Console CWV report&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Origin / URL groups&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Status trends, not full metric export&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Automation for &quot;today's field&quot;&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;CrUX API `queryRecord`&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Origin or URL&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;One collection period per call&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Metric definitions and thresholds&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;[Core Web Vitals guide](https://apogeewatcher.com/blog/what-are-core-web-vitals-a-practical-guide-for-2026)&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;Reference&lt;/p&gt;&lt;/td&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;1&quot;&gt;&lt;p&gt;N/A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2 id=&quot;crux-vis-and-the-crux-history-api-ttfb-inp-and-cls-field-history&quot; class=&quot;scroll-mt-24&quot;&gt;CrUX Vis and the CrUX History API: TTFB, INP, and CLS field history&lt;/h2&gt;&lt;p&gt;The old CrUX Dashboard's real value was trend lines: did INP drift after the tag manager change, did TTFB step up when the CDN rule shipped, did CLS calm down after the font fix? Current field snapshots in PageSpeed Insights answer &quot;where are we this week?&quot; History answers &quot;when did it start moving?&quot; Those are different tools now, and most teams need both answers on retainer accounts.&lt;/p&gt;&lt;h3&gt;CrUX Vis for weekly field trend charts&lt;/h3&gt;&lt;p&gt;CrUX Vis is Google's direct replacement UI. Point it at an origin or URL and open the Core Web Vitals, Loading Performance, Interactivity, or Visual Stability pages. Each chart shows weekly distributions built from the CrUX History API, with each point covering the previous 28 days of Chrome experiences. Data updates on Mondays; you can see up to about 40 weeks where Google has samples. TTFB sits with loading metrics; INP with interactivity; CLS with visual stability. CrUX Vis is free, fast compared with the old Looker report, and enough for a quarterly business review on one domain. It still queries one origin or URL at a time, so it does not replace a spreadsheet of fifty client homepages.&lt;/p&gt;&lt;h3&gt;Query the CrUX History API for programmatic field history&lt;/h3&gt;&lt;p&gt;The CrUX History API (&lt;code&gt;queryHistoryRecord&lt;/code&gt;) powers CrUX Vis and any custom chart you build. With a Google Cloud API key you can pull weekly time series for origins and, critically, for URLs that qualify, which monthly BigQuery alone did not make easy for page-level history. Set &lt;code&gt;collectionPeriodCount&lt;/code&gt; up to 40 to match the Vis window. Pair History API calls with daily &lt;code&gt;queryRecord&lt;/code&gt; calls when you want both &quot;what happened over the last six months&quot; and &quot;what changed since Tuesday's deploy.&quot; Google's &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/crux/guides/history-api&quot;&gt;History API guide&lt;/a&gt; documents form factors, histogram densities, and collection period dates; put those dates on client slides so nobody mistakes a four-week blend for a live score.&lt;/p&gt;&lt;h3&gt;BigQuery for long-archive Chrome UX Report analysis&lt;/h3&gt;&lt;p&gt;BigQuery (&lt;code&gt;chrome-ux-report&lt;/code&gt; public dataset) remains the long-archive option. Monthly tables still publish for deep analysis, competitive research, and custom Looker Studio reports on your own billing account. Load times are slower and the data is less current than the APIs, but you can join CrUX to analytics exports, slice by country or device tables, and keep years of origin history that exceed the History API window. Expect to pay for queries after the free tier unless you cache results. Some newer sub-metrics appear in APIs before every BigQuery table catches up; check &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/crux/release-notes&quot;&gt;CrUX release notes&lt;/a&gt; when you standardise a metric set for client reports.&lt;/p&gt;&lt;h2 id=&quot;crux-vis-crux-history-api-or-bigquery-which-fits-your-question&quot; class=&quot;scroll-mt-24&quot;&gt;CrUX Vis, CrUX History API, or BigQuery: which fits your question?&lt;/h2&gt;&lt;p&gt;Teams often ask for one replacement tool. In practice you pick by question, budget, and who needs to open the chart. Use CrUX Vis when a technical lead or SEO owner needs a trustworthy trend for one priority origin or URL and nobody wants to maintain a script. It is the fastest honest answer to &quot;show me INP over the last few months&quot; in a client call. Use the History API when you need the same series inside your own dashboard, a weekly email, or a database next to deploy tags. Use BigQuery when the question needs custom SQL, long horizons, or joins that Google's UI will never ship. Use PageSpeed Insights or the daily API when the question is simply whether today's field p75 cleared the budget.&lt;/p&gt;&lt;p&gt;None of these options shorten the 28-day rolling window. A fix shipped yesterday still ages into field aggregates gradually, which is why scheduled lab monitoring matters during the lag. &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/pagespeed-insights-vs-automated-monitoring-when-manual-checks-arent-enough&quot;&gt;PageSpeed Insights versus automated monitoring&lt;/a&gt; covers that split for teams who outgrew manual checks; the CrUX retirement does not remove the need for a lab clock beside the field clock.&lt;/p&gt;&lt;h2 id=&quot;third-party-chrome-ux-report-tools-for-agency-portfolios&quot; class=&quot;scroll-mt-24&quot;&gt;Third-party Chrome UX Report tools for agency portfolios&lt;/h2&gt;&lt;p&gt;Google is not the only place to chart CrUX. Several products wrap the History API or BigQuery with alerts, competitor views, and multi-URL boards. &lt;a rel=&quot;nofollow&quot; href=&quot;https://treo.sh/&quot;&gt;Treo&lt;/a&gt; is a common example for agencies that want CrUX-heavy field exploration and large URL lists without building pipes themselves. Field explorers and scheduled PageSpeed monitoring solve different jobs, and many teams use both. For how agencies shortlist monitoring tools on features and workflow fit, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/comparing-pagespeed-monitoring-tools-features-agencies-need&quot;&gt;comparing PageSpeed monitoring tools&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Apogee Watcher sits on the scheduled lab side of that split. We run PageSpeed Insights-backed tests across many sites on a cadence you set, store LCP, INP, CLS, FCP, and Total Blocking Time from lab runs, and show CrUX field numbers beside those results when Google returns them for the URL. We do not replace CrUX Vis for a six-month INP line chart on one domain, and we do not replicate BigQuery. We do give agency portfolios a repeatable before-and-after history on priority pages, budgets, and alerts while field data catches up, layered onto Search Console and the explorers you already use.&lt;/p&gt;&lt;p&gt;Field pipelines also slip. Collection delays, eligibility changes, and processing lag still show up in client inboxes as &quot;why is Search Console still amber?&quot; The same 28-day rolling window explains part of that lag after a deploy; see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/why-core-web-vitals-fix-not-in-crux-yet-28-day-window&quot;&gt;why your Core Web Vitals fix is not in CrUX yet&lt;/a&gt; for how to phrase timelines in client reports without blaming the wrong team.&lt;/p&gt;&lt;h2 id=&quot;what-no-free-chrome-ux-report-dashboard-replaces-after-retirement&quot; class=&quot;scroll-mt-24&quot;&gt;What no free Chrome UX Report dashboard replaces after retirement&lt;/h2&gt;&lt;p&gt;Being precise about gaps prevents another bookmark rot cycle. No free Google UI reproduces every old CrUX Dashboard view in one place. CrUX Vis does not list forty client origins on one screen. PageSpeed Insights does not chart six months of TTFB without you opening each URL. Search Console does not export weekly p75 series for every template. BigQuery is powerful but not free at scale and not friendly for account managers. The History API is accurate but requires engineering time.&lt;/p&gt;&lt;p&gt;Third-party tools close some of those holes with paid seats, URL limits, and their own refresh schedules. None of them remove CrUX eligibility rules: low-traffic pages still lack URL-level field data, desktop and phone still split, and rolling averages still trail deploys. The workable agency pattern we see is a small stack: CrUX Vis or a field explorer for deep history on hero URLs, Search Console for SEO status, scheduled lab monitoring for portfolio regression detection, and one sentence in the report that explains collection period dates. If nobody owns the threshold on a metric and nobody acts when it breaks, it should not be on the daily list, whether the chart came from Google or a vendor.&lt;/p&gt;&lt;h2 id=&quot;how-agencies-monitor-chrome-ux-report-field-metrics-across-client-sites&quot; class=&quot;scroll-mt-24&quot;&gt;How agencies monitor Chrome UX Report field metrics across client sites&lt;/h2&gt;&lt;p&gt;A practical Monday workflow after the dashboard retirement:&lt;/p&gt;&lt;ol start=&quot;1&quot;&gt;&lt;li&gt;&lt;p&gt;List priority origins and money URLs per client (home, pricing, signup, checkout, main templates). Match the list to URLs that actually receive Chrome traffic, not only the page engineering fixed in staging.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pin one field history view per key domain. CrUX Vis or a History API-backed explorer for quarterly reviews; note the collection period on the screenshot.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Schedule lab tests on the same URLs. Use budgets so a deploy regression triggers before field data moves. Frequency guidance lives in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/how-to-schedule-pagespeed-monitoring-test-frequency-priority-portfolio&quot;&gt;how to schedule PageSpeed monitoring&lt;/a&gt; if you are standardising cadence across accounts.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pull Search Console CWV status monthly for SEO owners. Treat it as a lagging group report, not a deploy gate.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Reserve BigQuery or custom API jobs for investigations (country regressions, device splits, before/after studies), not for every account manager every week.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Apogee Watcher automates steps two and three for multi-site organisations: discovery, scheduled tests, field slices when available, alerts, and client-ready exports without maintaining fifty PageSpeed Insights bookmarks. Start with a &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/check&quot;&gt;free domain check&lt;/a&gt; on one money URL if you want to see lab and field side by side before you add the portfolio, then expand the same URL list into budgets once the cadence feels right.&lt;/p&gt;&lt;h2 class=&quot;scroll-mt-24&quot;&gt;FAQ&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Is the CrUX Dashboard still available?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Google deprecated the shared CrUX Dashboard and turned off the CrUX Connector at the end of November 2025. You can copy the Looker Studio template and point it at your own BigQuery project, but the hosted connector most teams used no longer updates. CrUX Vis is Google's recommended first-party replacement for historical charts.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Where do I get field TTFB now that the dashboard is gone?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Check PageSpeed Insights field data for the latest rolling TTFB when Google publishes it for your origin or URL. For history, use CrUX Vis Loading Performance charts or query the CrUX History API for weekly TTFB series. BigQuery monthly tables remain available fo
&lt;/p&gt;</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24402/crux-dashboard-retired-where-to-get-ttfb-inp-and-field-history</guid>
<pubDate>Mon, 10 Aug 2026 17:57:00 +0000</pubDate>
</item>
<item>
<title>Your Website’s New Audience: AI Agents</title>
<link>https://coderlegion.com/23072/your-websites-new-audience-ai-agents</link>
<description>&lt;p&gt;Web performance has traditionally always been about human user experience. In recent time, that has evolved to include AI agents as well. AI agents can be thought of as anonymous and agentic systems that browse, consume and interact the web &lt;em&gt;on behalf&lt;/em&gt; of users. As more and more people continue to use AI agents or search engines pushing AI mode, it is recommended to optimize for agentic browsing as well.&lt;/p&gt;
&lt;p&gt;Google has recently included an &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/lighthouse/agentic-browsing/scoring&quot;&gt;agentic browsing&lt;/a&gt; category for both lighthouse and pagespeed insights, although they are currently experimental.&lt;/p&gt;
&lt;p&gt;How do we do that? Broadly speaking, there are a few basic steps that can be covered here:&lt;/p&gt;
&lt;h3 id=&quot;1-accessibility-tree-&quot;&gt;1. Accessibility Tree:&lt;/h3&gt;
&lt;p&gt;When agents browse your page, they do not typically &quot;see&quot; the page as a human would. They parse the DOM structure to analyze element roles, names and relationships. If any part of the tree is incomplete, it may lead to broken data and checkout flows. Google Lighthouse today measures this by the following methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every interactive element (button, input, link) needs to have a text node or &lt;code&gt;aria-label&lt;/code&gt; assigned to it so they are &quot;visible&quot; to agents.&lt;/li&gt;
&lt;li&gt;Parent - child relationships or sibling relations must be clear in the tree. For example, a label must be clearly tied to an input and so on. This is for better context of the general page structure and executing workflows.&lt;/li&gt;
&lt;li&gt;Interactive elements should NOT be hidden - A clickable Submit button hidden via CSS or &lt;code&gt;aria-label&lt;/code&gt; confuses agents.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-proper-metadata-&quot;&gt;2. Proper Metadata:&lt;/h3&gt;
&lt;p&gt;A &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/lighthouse/agentic-browsing/llms-txt&quot;&gt;llms.txt&lt;/a&gt; present at the root of your domain summarizes key pages and sections for the agent to browse for any given task without having to hit every page unnecessarily. There are more advanced techniques here like &lt;a rel=&quot;nofollow&quot; href=&quot;https://searchengineland.com/schema-markup-optimize-agentic-web-479080&quot;&gt;schema.org&lt;/a&gt; and llms-full.txt for a more enhanced summary.&lt;/p&gt;
&lt;h3 id=&quot;3-cumulative-layout-shift-cls-&quot;&gt;3. Cumulative Layout Shift (CLS):&lt;/h3&gt;
&lt;p&gt;Cumulative Layout Shift is already a part of traditional web performance metrics but for agents, it is even more crucial as layout stability ensures that the agent does not misclick or misfire anything in a given workflow or task.&lt;/p&gt;
&lt;h3 id=&quot;4-webmcp-adoption-&quot;&gt;4. WebMCP Adoption:&lt;/h3&gt;
&lt;p&gt;With WebMCP, agents are provided with APIs that enable them to directly take any action based on the user prompt. There are currently two ways to implement this - a declarative API, defined directly with HTML forms or imperative API, with JavaScript. This is currently available under an &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/blog/ai-webmcp-origin-trial&quot;&gt;origin trial&lt;/a&gt; - meaning you can actually try this out today in production with real users. This has practical use cases - like ecommerce, travel and general automation of day-to-day tasks.&lt;/p&gt;
&lt;h3 id=&quot;checking-if-your-website-is-agent-ready&quot;&gt;Checking if your website is agent ready&lt;/h3&gt;
&lt;p&gt;One really good resource I've found for checking this is &lt;a rel=&quot;nofollow&quot; href=&quot;https://isitagentready.com/&quot;&gt;https://isitagentready.com/&lt;/a&gt; - it gives you actionable insights and a prompt by checking against these aforementioned standards.&lt;/p&gt;
&lt;h3 id=&quot;closing-thoughts&quot;&gt;Closing thoughts&lt;/h3&gt;
&lt;p&gt;I'm not sure the future of the web will be fully autonomous (or if we would have evolved to some new interface) but for the moment, the best strategy is: build your page for humans and optimize the basics first - accessibility, structure, metadata, stability of the page. Then if and when the need arises, WebMCP and other technologies could be gradually introduced.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23072/your-websites-new-audience-ai-agents</guid>
<pubDate>Mon, 20 Jul 2026 11:53:25 +0000</pubDate>
</item>
<item>
<title>Why Your WordPress Site Is Slow (It Is Not Always Hosting)</title>
<link>https://coderlegion.com/22368/why-your-wordpress-site-is-slow-it-is-not-always-hosting</link>
<description>&lt;p&gt;When an enterprise WordPress site feels slow, the first meeting is almost always about infrastructure. Upgrade the server. Add a CDN. Move to a higher hosting tier. Those moves can help, and sometimes they are the right call. In our delivery work across hundreds of large WordPress sites, the limit is more often the code running on the stack: plugins loading assets where they are not needed, redundant database queries on every request, bloated autoloaded options, and third-party scripts executing before the page can paint. A faster machine does not remove that work. What follows is how to tell hosting from code, what to fix first, and how to keep performance from sliding back after the audit.&lt;/p&gt;&lt;h2 id=&quot;why-do-slow-wordpress-sites-get-blamed-on-hosting-first&quot; class=&quot;scroll-mt-24&quot;&gt;Why do slow WordPress sites get blamed on hosting first?&lt;/h2&gt;&lt;p&gt;Hosting is easy to see on a budget. It has a line item, a vendor logo, and a simple story: pay more, get more CPU. Stakeholders who do not live in the theme or plugin stack can point at the invoice when Core Web Vitals slip or when support tickets mention sluggish admin screens. Agencies inherit that framing when renewal season arrives or when a new CTO asks for a one-slide fix plan.&lt;/p&gt;&lt;p&gt;The story is not wrong in every case. Under-provisioned PHP workers, missing object cache, or a database server sharing hardware with a heavy marketing export job can cap every optimisation you make in the theme. The mistake is treating infrastructure as the default answer before anyone profiles a real URL under real traffic. PageSpeed Insights and Chrome DevTools still show whether time is spent waiting on the server (TTFB) or in render and script work on the client. If you skip that split, you risk funding a hosting upgrade that leaves LCP and INP unchanged because the homepage still loads twelve plugin stylesheets on a contact page.&lt;/p&gt;&lt;h2 id=&quot;when-is-a-hosting-or-cdn-upgrade-the-right-fix&quot; class=&quot;scroll-mt-24&quot;&gt;When is a hosting or CDN upgrade the right fix?&lt;/h2&gt;&lt;p&gt;Start with evidence, not tier names. Hosting is a sensible priority when TTFB stays high on cached or lightly dynamic templates after you have ruled out obvious query and autoload problems, when PHP-FPM queues spike under normal traffic, when Redis or Memcached is unavailable and the database becomes the session store for every visitor, or when the site has outgrown shared resources and error logs show timeouts rather than slow JavaScript tasks.&lt;/p&gt;&lt;p&gt;A CDN helps when static assets are served from a distant origin, when image bytes dominate LCP, or when you need predictable edge caching for anonymous HTML. It does not fix a plugin that enqueues analytics on every admin and front-end context, and it does not shrink a two-megabyte autoloaded options row. Pair infrastructure decisions with &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/quick-way-to-fix-lcp&quot;&gt;A Quick Way to Fix LCP: Four Changes That Cut Time to Paint&lt;/a&gt; so you separate server wait from paint path problems before you sign a new contract.&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Signal&lt;/th&gt;&lt;th&gt;Likely layer&lt;/th&gt;&lt;th&gt;First move&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;High TTFB on simple pages after code cleanup&lt;/td&gt;&lt;td&gt;Hosting, database, cache&lt;/td&gt;&lt;td&gt;Profile queries, enable object cache, review PHP pool sizing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Good TTFB, poor LCP/INP&lt;/td&gt;&lt;td&gt;Theme, plugins, third parties&lt;/td&gt;&lt;td&gt;Asset audit, script policy, template-specific enqueues&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Slow only on cart, checkout, or logged-in routes&lt;/td&gt;&lt;td&gt;Application logic, extensions&lt;/td&gt;&lt;td&gt;Profile those URLs; see [WooCommerce Performance Tuning (2026 Guide)](https://apogeewatcher.com/blog/woocommerce-performance-tuning-2026) for shop pages&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Regression after deploy, stable hosting bill&lt;/td&gt;&lt;td&gt;Code release&lt;/td&gt;&lt;td&gt;Diff enqueued assets and database changes; restore budgets&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2 id=&quot;what-code-problems-make-wordpress-slow-without-bad-hosting&quot; class=&quot;scroll-mt-24&quot;&gt;What code problems make WordPress slow without bad hosting?&lt;/h2&gt;&lt;p&gt;WordPress performance is a set of code and configuration choices, not a single hosting setting. These patterns appear repeatedly on enterprise sites that already run on capable managed hosts.&lt;/p&gt;&lt;h3&gt;Plugins that load assets globally&lt;/h3&gt;&lt;p&gt;Many extensions register CSS and JavaScript on &lt;code&gt;wp_enqueue_scripts&lt;/code&gt; without checking the template. A related-posts module meant for blog singles can still load on the homepage, checkout, and legal pages. Multiply that by thirty active plugins and the browser downloads hundreds of kilobytes before first paint on pages that never use the features. Query Monitor and asset inspection in DevTools show which handles appear on which URLs; the fix is conditional enqueues, dequeue rules, or removing the plugin when the feature is unused.&lt;/p&gt;&lt;h3&gt;Redundant or unindexed database queries&lt;/h3&gt;&lt;p&gt;Each uncached request can trigger dozens of queries from core, theme, and plugins. Poorly written custom code, meta queries without limits, and admin bar or SEO modules that scan large tables on front-end requests add latency that shows up as TTFB. Object cache reduces repeat cost but does not excuse N+1 patterns. Profile with Query Monitor on the slowest templates, fix or replace the worst offenders, and retest before buying new servers.&lt;/p&gt;&lt;h3&gt;Bloated autoloaded options&lt;/h3&gt;&lt;p&gt;WordPress loads all rows in &lt;code&gt;wp_options&lt;/code&gt; where &lt;code&gt;autoload = yes&lt;/code&gt; on every request. Plugins that store large JSON blobs, full import logs, or telemetry with autoload enabled can add megabytes to each page load. Inspect autoload size with standard diagnostics or database tools; set &lt;code&gt;autoload&lt;/code&gt; to &lt;code&gt;no&lt;/code&gt; for large keys that do not need to load on every hit, and delete stale option rows left by deactivated plugins.&lt;/p&gt;&lt;h3&gt;Third-party scripts before first paint&lt;/h3&gt;&lt;p&gt;Tag managers, chat widgets, A/B snippets, and personalisation vendors often inject synchronously or early in the document. They hurt INP and can delay LCP even when hosting is fast. &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/third-party-scripts-performance-worst-offenders&quot;&gt;Third-Party Scripts and Performance: How to Identify and Fix the Worst Offenders&lt;/a&gt; walks through ranking vendors and tightening load order; on WordPress, the same scripts are usually added by plugins or the theme customiser rather than by hand-coded tags.&lt;/p&gt;&lt;h3&gt;Theme and page-builder weight&lt;/h3&gt;&lt;p&gt;Elementor, Divi, WPBakery, and similar builders enqueue global bundles across templates. Enterprise sites keep them because marketing teams depend on the workflow, but the performance cost is real. Mitigations include asset optimisation plugins where they are reliable, stripping unused widgets, and hard rules about which templates may use the builder versus block templates for high-traffic routes.&lt;/p&gt;&lt;h2 id=&quot;how-do-you-audit-a-slow-wordpress-site-before-buying-infrastructure&quot; class=&quot;scroll-mt-24&quot;&gt;How do you audit a slow WordPress site before buying infrastructure?&lt;/h2&gt;&lt;p&gt;Treat the audit as an ordered checklist rather than a vague “make it faster” ticket.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pick representative URLs.&lt;/strong&gt; Homepage, primary conversion path, a heavy archive, and one logged-in or commerce route if applicable. Slow sitewide averages hide checkout pain.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run PageSpeed Insights on mobile and desktop.&lt;/strong&gt; Note TTFB, LCP, INP, and CLS separately. Save lab reports for before-and-after proof.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Profile server time.&lt;/strong&gt; Query Monitor on staging or a controlled production window; hosting dashboards for PHP slow logs and database wait.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;List active plugins and who owns each.&lt;/strong&gt; Mark business-critical extensions versus legacy installs nobody will defend in a meeting.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Measure autoloaded options size&lt;/strong&gt; and top database tables by row count where WooCommerce or custom post types dominate.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inventory third-party scripts&lt;/strong&gt; on conversion URLs with marketing modules enabled, not on a stripped staging theme.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Document findings in client language:&lt;/strong&gt; what changes behaviour, what changes cost, what needs a release window.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/core-web-vitals-monitoring-checklist-for-agencies&quot;&gt;Core Web Vitals monitoring checklist for agencies&lt;/a&gt; gives you release checks across clients; use it after the fix so a plugin update does not undo the audit quietly. For ongoing scope and cache pitfalls on WordPress sites, &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/wordpress-performance-monitoring-complete-guide&quot;&gt;WordPress Performance Monitoring: A Complete Guide&lt;/a&gt; covers what to watch between audits.&lt;/p&gt;&lt;h2 id=&quot;what-results-can-code-first-fixes-deliver&quot; class=&quot;scroll-mt-24&quot;&gt;What results can code-first fixes deliver?&lt;/h2&gt;&lt;p&gt;Outcomes vary by site, but code-led work can move metrics that hosting alone did not touch. In our experience on large WordPress projects we have seen concurrent user capacity roughly double after autoload cleanup, selective plugin removal, and object cache tuning on an unchanged server cluster. TTFB on key templates has fallen by about half when the main cost was query volume and options loaded on every request rather than CPU limits. Core Web Vitals have moved into passing bands on priority URLs without migrating data centres, because LCP and INP improved when we tightened assets and scripts, not when we bought faster hardware.&lt;/p&gt;&lt;p&gt;Those numbers are not a guarantee for your stack, but they show why buyers should fund a code audit before signing a more expensive hosting contract. Performance is a discipline built on standards, review, and measurement, not a plan tier.&lt;/p&gt;&lt;h2 id=&quot;how-do-you-keep-wordpress-fast-after-the-audit&quot; class=&quot;scroll-mt-24&quot;&gt;How do you keep WordPress fast after the audit?&lt;/h2&gt;&lt;p&gt;One-off cleanups decay without guardrails. Set plugin policy: who may install extensions, which categories need security review, and which routes must stay builder-free. Define asset rules: no new global enqueues without template justification. Add release checks: run synthetic PageSpeed tests on the priority URL list before major plugin updates, and compare results to the baseline you saved during the audit.&lt;/p&gt;&lt;p&gt;Translate thresholds into &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/performance-budget-thresholds-template&quot;&gt;performance budgets&lt;/a&gt; per template so regressions trigger a conversation instead of a surprise in Search Console. &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/how-to-schedule-pagespeed-monitoring-test-frequency-priority-portfolio&quot;&gt;How to schedule test frequency and priority across your portfolio&lt;/a&gt; explains cadence when you manage dozens of client sites; daily or weekly lab runs on mobile and desktop catch deploy drift long before field data moves.&lt;/p&gt;&lt;p&gt;Apogee Watcher fits agencies that need one schedule and history layer across many WordPress sites: a site per domain, discovery from sitemaps where exposed, PageSpeed Insights runs with CrUX context, and budget alerts when LCP or INP drifts on named paths. We layer on your existing host and tools; we do not replace server APM or WordPress admin. When you are ready to set up scheduled monitoring after the audit, start with &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/getting-started-apogee-watcher-step-by-step-setup-guide&quot;&gt;Getting Started with Apogee Watcher: A Step-by-Step Setup Guide&lt;/a&gt;.&lt;/p&gt;&lt;h2 class=&quot;scroll-mt-24&quot;&gt;FAQ&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Why is my WordPress site slow after I upgraded hosting?&lt;/strong&gt;Hosting upgrades improve server capacity and sometimes edge caching, but they do not remove heavy plugins, autoload bloat, or third-party scripts. If TTFB improved but LCP or INP did not, the next pass belongs in the theme and plugin stack, not another tier change.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;How do I tell if WordPress is slow because of plugins?&lt;/strong&gt;Compare the same URL with a minimal theme and plugins disabled on staging, or use Query Monitor to see per-plugin query time and hooks. If asset handles and query counts drop sharply, tighten plugin policy and load scripts only on the templates that need them.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Is enterprise WordPress performance mostly about hosting?&lt;/strong&gt;Not in our experience on large projects. Hosting sets a ceiling; code and configuration set most of the day-to-day latency on mixed marketing, membership, and commerce sites. Enterprise WordPress performance improves when engineering owns autoload, queries, and asset rules, not only when finance approves a new server.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Should I fix WordPress speed before Core Web Vitals monitoring?&lt;/strong&gt;Run a baseline first. Monitoring shows whether TTFB, LCP, or INP is the constraint and proves whether hosting or code changes worked. A short baseline before the audit and scheduled tests after fixes beat guessing from a single manual PageSpeed run.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;When should agencies push back on a hosting-only tender?&lt;/strong&gt;When lab data shows acceptable TTFB on priority URLs but poor paint or interaction metrics, or when autoload and plugin audits list concrete fixes that cost less than a multi-year hosting contract. Bring evidence from scheduled tests and a phased code plan buyers can compare line by line.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Next step:&lt;/strong&gt; Run PageSpeed Insights on three priority WordPress URLs, note whether TTFB or client-side metrics fail first, then work through the audit list before approving infrastructure spend. &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/&quot;&gt;Start monitoring those URLs on a schedule&lt;/a&gt; so the fix survives the next plugin update.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22368/why-your-wordpress-site-is-slow-it-is-not-always-hosting</guid>
<pubDate>Mon, 13 Jul 2026 12:31:00 +0000</pubDate>
</item>
<item>
<title>WooCommerce Performance Tuning (2026 Guide)</title>
<link>https://coderlegion.com/22112/woocommerce-performance-tuning-2026-guide</link>
<description>&lt;p&gt;WooCommerce performance tuning is not a single plugin install or a one-off PageSpeed Insights paste. Your shop runs on WordPress hosting, a theme or page builder, dozens of extensions, and dynamic routes that full-page cache cannot treat like the homepage. Shoppers still judge you on the same signals Google publishes: &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/tag/lcp&quot;&gt;Largest Contentful Paint&lt;/a&gt; on product galleries, &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/tag/inp&quot;&gt;Interaction to Next Paint&lt;/a&gt; on variant pickers and checkout fields, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/tag/cls&quot;&gt;Cumulative Layout Shift&lt;/a&gt; when cart badges and promo bars load late.&lt;/p&gt;&lt;p&gt;Slow WooCommerce stores pay in conversion, not only rankings. In our experience and in published retail studies, each extra second of load time on mobile can trim conversions by roughly three to seven per cent, with wider spreads on checkout-heavy traffic. Published retail speed research summarised on &lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/case-studies/milliseconds-make-millions&quot;&gt;web.dev&lt;/a&gt; links small load-time improvements to measurable funnel movement; mobile shoppers tolerate less delay than desktop benchmarks suggest. What follows is a prioritised 2026 playbook: infrastructure first, then data structure, caching, media, and extensions, with Core Web Vitals monitoring on the URLs that actually carry revenue. For funnel-wide metric priorities, pair this with &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/ecommerce-performance-monitoring-what-metrics-matter&quot;&gt;Performance Monitoring for E-Commerce: What Metrics Matter Most&lt;/a&gt;; for ongoing WordPress scope and cache pitfalls, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/wordpress-performance-monitoring-complete-guide&quot;&gt;WordPress Performance Monitoring: A Complete Guide&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;which-woocommerce-urls-should-you-tune-and-monitor-first&quot; class=&quot;scroll-mt-24&quot;&gt;Which WooCommerce URLs should you tune and monitor first?&lt;/h2&gt;&lt;p&gt;Sitewide averages hide checkout pain. Prioritise optimisation and scheduled tests in this order:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Product detail pages (PDPs):&lt;/strong&gt; heavy images, variant UI, reviews; primary LCP and INP risk.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shop and category archives:&lt;/strong&gt; many thumbnails, filters, pagination or infinite scroll.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cart:&lt;/strong&gt; cart fragments, coupons, cross-sells; INP and CLS even when LCP looks fine.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Checkout and account:&lt;/strong&gt; payment scripts, address fields, logged-in flows; often uncached.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Homepage and campaign landers:&lt;/strong&gt; important for acquisition, but secondary to commerce paths.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Document the list per client during onboarding. A store that only tunes the homepage will miss the PDP regression that support tickets mention first. &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/mobile-vs-desktop-core-web-vitals-monitoring-both&quot;&gt;Mobile vs desktop Core Web Vitals monitoring&lt;/a&gt; matters here because WooCommerce traffic skews mobile on many catalogues.&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Page type&lt;/th&gt;&lt;th&gt;Metrics to weight&lt;/th&gt;&lt;th&gt;Typical issues&lt;/th&gt;&lt;th&gt;Priority&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Product detail&lt;/td&gt;&lt;td&gt;LCP, INP, CLS&lt;/td&gt;&lt;td&gt;Large gallery images, variant selectors, review widgets&lt;/td&gt;&lt;td&gt;Very high&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Product listing&lt;/td&gt;&lt;td&gt;LCP, INP&lt;/td&gt;&lt;td&gt;Thumbnail grids, faceted filters, infinite scroll&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cart&lt;/td&gt;&lt;td&gt;INP, CLS&lt;/td&gt;&lt;td&gt;Cart fragments, mini-cart updates, upsell modules&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Checkout&lt;/td&gt;&lt;td&gt;INP, TTFB&lt;/td&gt;&lt;td&gt;Payment gateways, address validation, third-party scripts&lt;/td&gt;&lt;td&gt;Very high&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2 id=&quot;does-woocommerce-hosting-limit-how-fast-you-can-go&quot; class=&quot;scroll-mt-24&quot;&gt;Does WooCommerce hosting limit how fast you can go?&lt;/h2&gt;&lt;p&gt;Hosting is the ceiling for everything below. Shared plans with crowded neighbours, PHP without opcode cache, and no object cache force WooCommerce to hit MySQL on every cart calculation. Managed WordPress hosts that target commerce usually ship Redis or Memcached, tuned PHP-FPM pools, and edge caching that respects dynamic routes.&lt;/p&gt;&lt;p&gt;Agencies we work with commonly deploy stores on &lt;strong&gt;Kinsta&lt;/strong&gt;, &lt;strong&gt;WP Engine&lt;/strong&gt;, or &lt;strong&gt;Cloudways&lt;/strong&gt; (Vultr HF or DigitalOcean profiles), but the decision criteria matter more than the logo: object cache available, sensible PHP version (8.2+ in 2026), staging that mirrors production CDN rules, and support that understands WooCommerce cache exclusions. WP Engine’s dynamic plugin loading can reduce front-end weight on supported stacks; verify on your client’s plan rather than assuming it is enabled.&lt;/p&gt;&lt;p&gt;If TTFB stays high after theme and plugin cleanup, profile hosting before micro-optimising CSS. &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/third-party-scripts-performance-worst-offenders&quot;&gt;Third-party scripts and performance&lt;/a&gt; helps when the bottleneck is JavaScript, but a two-second server wait on checkout is often infrastructure or database query volume, not a missing defer attribute.&lt;/p&gt;&lt;h2 id=&quot;why-should-you-enable-high-performance-order-storage-hpos&quot; class=&quot;scroll-mt-24&quot;&gt;Why should you enable High-Performance Order Storage (HPOS)?&lt;/h2&gt;&lt;p&gt;High-Performance Order Storage moves order data out of the generic &lt;code&gt;wp_posts&lt;/code&gt; and &lt;code&gt;wp_postmeta&lt;/code&gt; tables into dedicated WooCommerce tables designed for order queries. WooCommerce’s own benchmarks report up to five times faster order creation, roughly one and a half times faster checkout in some configurations, and up to forty times faster order queries in the admin after migration. Those wins concentrate on order persistence and back-office screens rather than storefront LCP, but checkout can still feel snappier when writes were the bottleneck.&lt;/p&gt;&lt;p&gt;Enable it under &lt;strong&gt;WooCommerce → Settings → Advanced → Features&lt;/strong&gt; when compatibility checks pass for your payment and fulfilment plugins. HPOS is one of the highest leverage database changes available in 2026 with limited theme impact, because it optimises back-office and order persistence rather than front-end templates. Run it before aggressive plugin pruning if orders are already large; the win compounds as transaction volume grows.&lt;/p&gt;&lt;h2 id=&quot;how-should-woocommerce-caching-differ-from-a-brochure-wordpress-site&quot; class=&quot;scroll-mt-24&quot;&gt;How should WooCommerce caching differ from a brochure WordPress site?&lt;/h2&gt;&lt;p&gt;You cannot cache every URL identically on WooCommerce. Anonymous visitors may receive a fast cached HTML shell on the shop archive, while cart, checkout, and my-account must bypass full-page cache or you will serve another shopper’s session semantics. For &lt;strong&gt;page cache&lt;/strong&gt;, cache homepage, shop, categories, and PDPs where your host or plugin allows, and &lt;strong&gt;exclude&lt;/strong&gt; cart, checkout, my-account, and often add-to-cart endpoints. Document exclusions in client notes; our &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/site-audit-checklist-onboarding-client-performance-monitoring&quot;&gt;site audit checklist for onboarding&lt;/a&gt; includes stack fields for exactly this.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Object cache:&lt;/strong&gt; enable &lt;strong&gt;Redis&lt;/strong&gt; (or Memcached) when the host supports it. WooCommerce sessions, transients, and product lookups benefit disproportionately. Without object cache, every fragment refresh can multiply database reads.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Cart fragments:&lt;/strong&gt; WooCommerce refreshes mini-cart markup via &lt;code&gt;wc-ajax=get_refreshed_fragments&lt;/code&gt; on many page loads. That AJAX call is a common hidden INP and TTFB tax. Mitigations include conditional loading (only when the cart has items), disabling fragments on non-shop templates, or dedicated plugins and snippets that stop global refreshes. Measure before and after on the homepage and a PDP; fragments often hurt pages that never show a cart icon.&lt;/p&gt;&lt;p&gt;Popular WooCommerce-aware caching stacks include &lt;strong&gt;WP Rocket&lt;/strong&gt;, &lt;strong&gt;LiteSpeed Cache&lt;/strong&gt; on LiteSpeed hosts, and host-native edge layers. Rules matter more than brand: verify cart/checkout bypass, respect WooCommerce cookies, and purge product pages when stock or sale price changes.&lt;/p&gt;&lt;h2 id=&quot;how-do-you-fix-lcp-on-woocommerce-product-pages&quot; class=&quot;scroll-mt-24&quot;&gt;How do you fix LCP on WooCommerce product pages?&lt;/h2&gt;&lt;p&gt;Product galleries dominate LCP. Upload appropriately sized masters, serve &lt;strong&gt;WebP&lt;/strong&gt; or AVIF with fallbacks, and use responsive &lt;code&gt;srcset&lt;/code&gt; so mobile does not download desktop widths. Compress before upload with tools such as ShortPixel, Imagify, or similar; lazy-load below-the-fold gallery images but &lt;strong&gt;not&lt;/strong&gt; the primary LCP candidate without measuring first.&lt;/p&gt;&lt;p&gt;Reserve explicit width and height on product images to protect CLS when thumbnails load. A CDN such as Cloudflare in front of static assets reduces latency for image-heavy catalogues; pair CDN work with &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/image-optimisation-strategies-better-lcp-scores&quot;&gt;image optimisation strategies for better LCP&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Theme and builder debt still matters. Elementor, Divi, and WPBakery layouts often enqueue global CSS and JavaScript on PDP templates. Audit sections you do not use; each active block can add render cost even when it looks empty on mobile.&lt;/p&gt;&lt;h2 id=&quot;what-causes-poor-inp-and-cls-on-woocommerce-shops&quot; class=&quot;scroll-mt-24&quot;&gt;What causes poor INP and CLS on WooCommerce shops?&lt;/h2&gt;&lt;p&gt;INP rises when the main thread is busy during taps: variant changes that synchronously recalculate price, filters that re-render entire grids, and cart drawer scripts that fetch recommendations before showing contents. Profile with Query Monitor and Chrome DevTools performance recordings on throttled mobile; fix the longest tasks before debating INP thresholds with stakeholders. CLS appears when star ratings, consent banners, promo bars, or cart count badges inject without reserved space. Sticky headers plus announcement bars compete for the same viewport on small screens. Measure CLS on PDP and checkout with production marketing modules enabled, not on a stripped staging theme.&lt;/p&gt;&lt;h2 id=&quot;when-should-you-audit-woocommerce-plugins-and-themes&quot; class=&quot;scroll-mt-24&quot;&gt;When should you audit WooCommerce plugins and themes?&lt;/h2&gt;&lt;p&gt;Plugin count alone is not a score, but thirty to sixty active plugins with global asset loading is a pattern we see on struggling stores. Deactivate and delete unused extensions quarterly. Use &lt;strong&gt;Query Monitor&lt;/strong&gt; to attribute slow queries and hooks to specific plugins. Prefer lightweight block themes or lean classic themes when rebuild budget exists; heavy builders are workable only with strict asset rules.&lt;/p&gt;&lt;p&gt;Load scripts and styles only on templates that need them. A related-posts plugin enqueuing on checkout helps nobody. WP Engine’s dynamic plugin loading is one hosted answer; on other stacks you achieve similar outcomes with manual dequeue rules or optimisation plugins that map assets to page types.&lt;/p&gt;&lt;h2 id=&quot;how-do-you-keep-the-woocommerce-database-from-slowing-checkout&quot; class=&quot;scroll-mt-24&quot;&gt;How do you keep the WooCommerce database from slowing checkout?&lt;/h2&gt;&lt;p&gt;Beyond HPOS, schedule maintenance: prune expired transients, limit post revisions, remove spam comments, and archive stale order notes when plugins allow. &lt;strong&gt;WP-Optimize&lt;/strong&gt; and &lt;strong&gt;Advanced Database Cleaner&lt;/strong&gt; are common choices; run destructive cleans on staging first.&lt;/p&gt;&lt;p&gt;Monitor slow queries after sales events. Object cache reduces repeat cost, but a missing index on a custom plugin table still hurts checkout under load. Pair database hygiene with scheduled synthetic tests on checkout so regressions surface before month-end reporting.&lt;/p&gt;&lt;h2 id=&quot;quick-wins-versus-advanced-woocommerce-performance-work&quot; class=&quot;scroll-mt-24&quot;&gt;Quick wins versus advanced WooCommerce performance work&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Quick wins (days, not months):&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Enable HPOS after compatibility review.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Configure page cache with cart/checkout/account exclusions.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Reduce or conditionalise cart fragments.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Convert product images to modern formats; lazy-load non-LCP images.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Remove unused plugins; fix one slow query flagged by Query Monitor.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Advanced work (weeks, higher cost):&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Migrate to commerce-focused hosting with Redis and tuned PHP.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Rebuild or replace a heavy page-builder theme.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Implement critical CSS, defer non-essential JavaScript, and fine-tune Redis cache groups.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Headless or custom checkout front ends only when revenue and team size justify the operational cost; most stores below roughly three million dollars in annual revenue improve faster by tuning the monolith they already run.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;how-do-you-measure-woocommerce-core-web-vitals-after-tuning&quot; class=&quot;scroll-mt-24&quot;&gt;How do you measure WooCommerce Core Web Vitals after tuning?&lt;/h2&gt;&lt;p&gt;Use &lt;strong&gt;PageSpeed Insights&lt;/strong&gt; and &lt;strong&gt;CrUX&lt;/strong&gt; for field percentiles where Google publishes data. Use &lt;strong&gt;WebPageTest&lt;/strong&gt; or &lt;strong&gt;GTmetrix&lt;/strong&gt; for deep waterfall debugging on PDP and checkout. Use &lt;strong&gt;Query Monitor&lt;/strong&gt; during active development.&lt;/p&gt;&lt;p&gt;For production guardrails, schedule &lt;strong&gt;synthetic lab runs&lt;/strong&gt; on the URL list above with separate mobile and desktop strategies. Lab data catches deploy and plugin regressions within hours; CrUX lags and may omit low-traffic checkout URLs. Both belong in client conversations.&lt;/p&gt;&lt;p&gt;Set explicit &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/the-complete-guide-to-performance-budgets-for-web-teams&quot;&gt;performance budgets&lt;/a&gt; per template using the &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/performance-budget-thresholds-template&quot;&gt;performance budget thresholds template&lt;/a&gt;; tighten bands on PDP and checkout compared with policy pages. &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/how-to-schedule-pagespeed-monitoring-test-frequency-priority-portfolio&quot;&gt;How to schedule test frequency and priority across your portfolio&lt;/a&gt; explains cadence when you manage multiple shops.&lt;/p&gt;&lt;p&gt;Apogee Watcher fits agencies that need one schedule and history layer across many WooCommerce clients: a site per store, discovery from sitemaps where exposed, mobile and desktop PageSpeed Insights runs with CrUX context, and budget alerts when LCP or INP drifts on named URLs. We do not replace WooCommerce analytics or server APM; we give you repeatable lab evidence and client-ready trends without juggling separate PageSpeed Insights tabs per domain. Setup steps are in &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/getting-started-apogee-watcher-step-by-step-setup-guide&quot;&gt;Getting Started with Apogee Watcher: A Step-by-Step Setup Guide&lt;/a&gt;. The &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/core-web-vitals-monitoring-checklist-for-agencies&quot;&gt;Core Web Vitals monitoring checklist for agencies&lt;/a&gt; mirrors the release gate below across clients.&lt;/p&gt;&lt;h2 id=&quot;woocommerce-performance-tuning-checklist&quot; class=&quot;scroll-mt-24&quot;&gt;WooCommerce performance tuning checklist&lt;/h2&gt;&lt;p&gt;Use before peak season, a major plugin install, or a theme update:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Mobile and desktop lab runs on homepage, top category, top three PDPs, cart, and checkout path.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;HPOS enabled; compatibility plugins verified.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Cache exclusions confirmed for cart, checkout, and account.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Cart fragment behaviour measured; conditional or disabled where safe.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;LCP element identified on best-selling PDP; dimensions and format verified.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;INP spot-check on variant change, filter toggle, and proceed-to-checkout.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;CLS check with promo bar, consent banner, and cart badge enabled.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Budgets and alert recipients set per template; baseline stored with owner.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 class=&quot;scroll-mt-24&quot;&gt;FAQ&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;What is WooCommerce performance tuning?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;It is the combined work of hosting configuration, database structure (including HPOS), caching rules, media optimisation, and plugin hygiene so shop, product, cart, and checkout routes stay within acceptable Core Web Vitals and conversion-friendly load times. Monitoring is part of tuning, not a separate phase after launch.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Does HPOS improve front-end Core Web Vitals?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;HPOS primarily accelerates order storage and admin queries. Checkout can feel snappier when order writes were the bottleneck, but LCP and INP on PDP still depend on theme assets and scripts. Enable HPOS for database health; measure vitals separately on storefront URLs.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Why is WooCommerce cart so slow with caching enabled?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Cart and checkout are dynamic. If page cache serves them incorrectly, you see functional bugs; if they bypass cache correctly, they run full PHP and hit cart fragments, sessions, and payment scripts. Tune fragments, object cache, and script load on those routes instead of forcing them into homepage cache rules.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;What LCP target should WooCommerce stores use?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Field guidance treats LCP at or below 2.5 seconds at the 75th percentile as good. Lab targets on PDP during optimisation sprints can be stricter. Compare mobile separately; many shops see most revenue on phones.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;How is WooCommerce performance tuning different from Shopify speed work?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;The vitals are the same; the stack differs. WooCommerce on WordPress combines self-managed plugins, hosting choice, and cache exclusions you configure. &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/shopify-speed-optimization-core-web-vitals-ecommerce&quot;&gt;Shopify speed optimization&lt;/a&gt; centralises more theme constraints; WooCommerce trades flexibility for responsibility to audit extensions and database shape yourself.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Should agencies monitor every WooCommerce URL?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;No. Start with ten to twenty priority URLs per store, weighted to PDP, revenue categories, cart, and checkout. Expand when alert triage stays manageable.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Can improving Core Web Vitals increase WooCommerce sales?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Industry studies link small speed gains to funnel movement; your client’s uplift depends on baseline speed and traffic quality. Use scheduled monitoring to prove before-and-after on their templates rather than citing averages alone.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;WooCommerce performance tuning that lasts pairs HPOS and Redis-aware caching with honest cache exclusions, a ruthless plugin audit, and image work on PDPs. Fix cart and checkout INP before debating homepage hero animations, then keep mobile lab history on the URLs that pay the bills.&lt;/p&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/sign-up&quot;&gt;Start a free Apogee Watcher account&lt;/a&gt; to schedule mobile and desktop tests with vitals budgets on WooCommerce client sites, or run a &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/check&quot;&gt;free domain PageSpeed check&lt;/a&gt; on a shop before you scope the first optimisation
&lt;/p&gt;</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22112/woocommerce-performance-tuning-2026-guide</guid>
<pubDate>Fri, 10 Jul 2026 12:39:00 +0000</pubDate>
</item>
<item>
<title>Are We Visible in ChatGPT? What Agencies Can Measure First</title>
<link>https://coderlegion.com/22114/are-we-visible-in-chatgpt-what-agencies-can-measure-first</link>
<description>&lt;p&gt;The board slide says &quot;AI visibility&quot; in bold. The client wants a yes or no: are we showing up in ChatGPT, Perplexity, or Google's AI Overviews? Your team wants a defensible answer that does not require procuring another SaaS category before Friday's call. What follows is a practical split agencies use in delivery: what belongs in probabilistic citation tracking, what belongs in deterministic technical monitoring, and what you can measure in the first week with tools you likely already run.&lt;/p&gt;&lt;h2 id=&quot;what-does-ai-search-visibility-mean-when-a-client-asks-about-chatgpt&quot; class=&quot;scroll-mt-24&quot;&gt;What does &quot;AI search visibility&quot; mean when a client asks about ChatGPT?&lt;/h2&gt;&lt;p&gt;Clients usually mean one of three things, and they rarely say which:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Citation:&lt;/strong&gt; Does an AI product mention our brand or link to our pages when someone asks a buyer-style question?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Retrieval:&lt;/strong&gt; Can AI crawlers fetch and parse our priority URLs fast enough to include them in indexes or training corpora?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Traditional search:&lt;/strong&gt; Are we losing clicks because AI Overviews answer the query without a visit?&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Those are related but not interchangeable. A green &quot;visibility score&quot; from a GEO dashboard answers (1) for a fixed prompt list. Server logs and scheduled PageSpeed tests answer (2) for URLs you choose. Search Console and third-party CTR studies answer (3) at the query level, with known gaps. Mixing them in one slide produces arguments in week two when checkout is slow but the homepage prompt chart looks fine.&lt;/p&gt;&lt;p&gt;Agencies that answer well start by naming the layer, then pick metrics that match the layer. That framing sets honest expectations: no single vendor owns all three today, and a homepage PageSpeed Insights paste is not citation analytics. If the SOW says &quot;AI visibility&quot; without defining the layer, clarify before you buy tools.&lt;/p&gt;&lt;h2 id=&quot;ai-search-visibility-tools-citations-prompts-and-geo-measurement&quot; class=&quot;scroll-mt-24&quot;&gt;AI search visibility tools: citations, prompts, and GEO measurement&lt;/h2&gt;&lt;p&gt;Probabilistic measurement asks whether a model or AI search product mentions your brand or URLs for prompts that resemble how buyers research. GEO (generative engine optimisation) platforms, prompt trackers, and manual spot checks in ChatGPT or Perplexity live here. Treat outputs as directional trends, not fixed ranks.&lt;/p&gt;&lt;p&gt;Strengths of this layer:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Shows whether you appear for category-style questions (&quot;best X for agencies&quot;, &quot;tools to monitor Core Web Vitals&quot;).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Helps compare before and after content or positioning changes when you hold the prompt set constant.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Gives account managers a chart when leadership asks for &quot;AI SEO.&quot;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Limits you should say out loud on client calls:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Prompt choice dominates the result. A dashboard tracking &quot;best pagespeed monitoring tool 2026&quot; tells you almost nothing about whether you appear when a technical lead asks how to monitor forty Shopify stores after a theme deploy. Generic prompt lists produce tidy graphs with weak buyer context; agency-real questions include client vertical, stack, and trigger event.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Models drift. The same prompt can yield different answers by phrasing, session, and model version. Weekly graphs are useful for trends, not courtroom proof.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Citation is not retrieval. Being mentioned in an answer does not prove crawlers can reliably fetch your product pages, docs, or pricing routes today.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Use GEO or AI visibility SaaS when the retainer includes citation reporting, when content strategy depends on LLM mentions, or when leadership funds a dedicated prompt library maintained by someone who understands the client's ICP. Layer it beside technical monitoring; do not replace crawl and speed work with prompt scores alone. Pair those tools with the posts below when clients ask about traffic impact or crawler access.&lt;/p&gt;&lt;p&gt;For how AI-mediated search affects clicks on traditional results, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/ai-overviews-are-killing-clicks-what-the-data-shows-and-how-to-respond&quot;&gt;AI Overviews Are Killing Clicks: What the Data Shows and How to Respond&lt;/a&gt;. For why fetchability precedes citation, see &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/why-ai-crawlers-need-fast-crawlable-pages-and-how-to-stay-ready&quot;&gt;Why AI Crawlers Need Fast, Crawlable Pages&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;website-for-ai-crawlers-deterministic-signals-agencies-can-measure-first&quot; class=&quot;scroll-mt-24&quot;&gt;Website for AI crawlers: deterministic signals agencies can measure first&lt;/h2&gt;&lt;p&gt;Deterministic measurement asks whether priority URLs respond correctly and quickly when bots request them, and whether that state persists after deploys. This is the layer agencies can often cover in the first week without a GEO contract, because it reuses performance monitoring, crawl hygiene, and reporting rhythms you already sell. Most delivery teams already know how to run these checks; the gap is usually scope and scheduling, not methodology.&lt;/p&gt;&lt;p&gt;What fits here:&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Signal&lt;/th&gt;&lt;th&gt;What it tells you&lt;/th&gt;&lt;th&gt;Typical source&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GPTBot / Googlebot allowed on public URLs&lt;/td&gt;&lt;td&gt;Crawlers permitted to fetch content you want considered&lt;/td&gt;&lt;td&gt;`robots.txt`, server logs&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;HTTP status and redirect chains&lt;/td&gt;&lt;td&gt;No 404/5xx on docs, pricing, product templates&lt;/td&gt;&lt;td&gt;Scheduled fetch or crawl audit&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Lab LCP, INP, CLS on priority URLs&lt;/td&gt;&lt;td&gt;Page likely parseable within crawler timeouts; stable layout&lt;/td&gt;&lt;td&gt;PageSpeed Insights, scheduled monitoring&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Regression after release&lt;/td&gt;&lt;td&gt;Theme, app, or CDN change broke a route AI systems rely on&lt;/td&gt;&lt;td&gt;Alerts on budget breach&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Mobile vs desktop split&lt;/td&gt;&lt;td&gt;Crawlers often behave like lightweight clients&lt;/td&gt;&lt;td&gt;Separate lab strategies&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;This layer does not prove you will be cited. It proves you are not failing the step before citation: access. That distinction keeps you credible when a client shows a competitor's GEO screenshot, and it gives you a concrete follow-up when citation matters but checkout returns 503 on mobile lab tests this week.&lt;/p&gt;&lt;h2 id=&quot;gptbot-and-robotstxt-ai-crawler-access-on-client-sites&quot; class=&quot;scroll-mt-24&quot;&gt;GPTBot and robots.txt: AI crawler access on client sites&lt;/h2&gt;&lt;p&gt;Before you debate prompt libraries, confirm the client has not accidentally blocked the bots they care about. OpenAI documents &lt;a rel=&quot;nofollow&quot; href=&quot;https://platform.openai.com/docs/gptbot&quot;&gt;GPTBot&lt;/a&gt; in &lt;code&gt;robots.txt&lt;/code&gt;; Google publishes &lt;a rel=&quot;nofollow&quot; href=&quot;https://developers.google.com/search/docs/crawling-indexing/overview-google-crawlers&quot;&gt;crawler guidance&lt;/a&gt; for Search and related products. A five-minute fetch of production &lt;code&gt;robots.txt&lt;/code&gt; prevents expensive arguments later.&lt;/p&gt;&lt;p&gt;Agency checklist for a client kickoff:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Fetch production &lt;code&gt;robots.txt&lt;/code&gt; and note &lt;code&gt;Disallow&lt;/code&gt; rules for GPTBot, Google-Extended, and other AI user-agents the client mentions.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Confirm &lt;strong&gt;public&lt;/strong&gt; marketing, docs, and pricing paths are allowed unless the client explicitly opts out of AI training or retrieval.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Document opt-out decisions in writing. Blocking GPTBot is a valid policy choice; pretending the site is &quot;optimised for AI&quot; while disallowing crawlers is not.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Re-check after CMS or SEO plugin updates. All-in-one SEO tools sometimes ship aggressive bot blocks.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Keep a one-page bot policy per client next to the URL inventory. Account managers should know whether the retainer includes &quot;AI crawler access review&quot; or only performance monitoring. Update both documents when the client changes CMS, SEO plugins, or hosting.&lt;/p&gt;&lt;h2 id=&quot;core-web-vitals-drift-response-time-and-crawl-errors-on-priority-urls&quot; class=&quot;scroll-mt-24&quot;&gt;Core Web Vitals drift, response time, and crawl errors on priority URLs&lt;/h2&gt;&lt;p&gt;Slow responses and hard errors are the fastest way to lose fetchability. Crawlers work under timeouts; a pricing page that takes eight seconds in lab conditions or returns intermittent 503s may simply drop out of consideration while the homepage still looks fine. That pattern shows up often when monitoring stops at the homepage.&lt;/p&gt;&lt;p&gt;Prioritise URLs by business intent, not sitemap size:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Homepage and primary category landers&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pricing and comparison pages&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Documentation and help centres cited in sales&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Product detail or template pages for ecommerce clients&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Login and checkout only where publicly reachable tests are allowed&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Run mobile and desktop lab tests on that list. Track &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/what-are-core-web-vitals-a-practical-guide-for-2026&quot;&gt;Core Web Vitals&lt;/a&gt; (LCP, INP, CLS) as indicators of parseability and stability, not as magic &quot;AI ranking&quot; levers. Google’s AI Overviews documentation emphasises content quality and relevance for visibility; it does not list CWV as a citation factor. CWV still matters because slow, unstable pages fail fetches and frustrate human visitors who click through from any surface.&lt;/p&gt;&lt;p&gt;Set provisional budgets and review weekly during AI visibility engagements, the same way you would during a Core Web Vitals remediation project. When LCP regresses after a hero image change, that belongs in the client report beside any GEO chart. Regressions on docs or checkout URLs matter more for fetchability than a flat homepage score.&lt;/p&gt;&lt;h2 id=&quot;javascript-rendered-content-and-scheduled-pagespeed-lab-tests&quot; class=&quot;scroll-mt-24&quot;&gt;JavaScript-rendered content and scheduled PageSpeed lab tests&lt;/h2&gt;&lt;p&gt;Many AI crawlers do not execute a full browser. They rely on initial HTML and simplified rendering. If your client's docs or product UI mount content client-side only, the crawler may see an empty shell while a manual visit in Chrome looks fine.&lt;/p&gt;&lt;p&gt;Lab tests through PageSpeed Insights or Lighthouse do not perfectly simulate GPTBot, but they flag render-blocking scripts, long tasks, and late LCP elements that correlate with fetch problems. Pair lab schedules with occasional log review for AI user-agents to see which URLs they request and whether status codes stay clean. When stakeholders ask for &quot;AI-ready&quot; pages, translate that into operational requirements: critical copy in HTML, sane response times, stable layout, and monitoring on the URLs where answers should come from.&lt;/p&gt;&lt;h2 id=&quot;agency-seo-reporting-first-week-ai-visibility-checklist&quot; class=&quot;scroll-mt-24&quot;&gt;Agency SEO reporting: first-week AI visibility checklist&lt;/h2&gt;&lt;p&gt;Use this sequence on a new or upsold AI visibility thread:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Day 1 to 2: Scope and access&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Confirm what &quot;visible&quot; means in the SOW: citations, crawl access, traffic, or all three.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Export or build a priority URL list (ten to twenty URLs, not the whole sitemap).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Audit &lt;code&gt;robots.txt&lt;/code&gt; and document bot policy.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Day 3 to 4: Baseline deterministic signals&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Run lab tests on priority URLs mobile and desktop.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Note HTTP status, redirect hops, and obvious render issues.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Optional: run the public &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/check&quot;&gt;free domain PageSpeed check&lt;/a&gt; or the &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/free-domain-pagespeed-check-multi-page-reports&quot;&gt;multi-page domain report&lt;/a&gt; for a client-ready PDF before you configure schedules.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Day 5: Client readout&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Slide 1: layers diagram (probabilistic vs deterministic).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Slide 2: URL list and bot policy summary.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Slide 3: lab baseline table with three named actions (owner + date).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Slide 4: optional GEO tool timeline if budget approved; honest gap if not.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Week 2 onward&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Schedule recurring tests and wire alerts.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Add GEO or prompt tracking when citation reporting is in scope.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Revisit URL list when the client ships new templates or docs IA.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;That cadence gives marketing a story and engineering a backlog without overclaiming day-one citation certainty.&lt;/p&gt;&lt;h2 id=&quot;pagespeed-monitoring-vs-geo-tools-for-ai-search-visibility&quot; class=&quot;scroll-mt-24&quot;&gt;PageSpeed monitoring vs GEO tools for AI search visibility&lt;/h2&gt;&lt;p&gt;One-off PageSpeed Insights tabs do not survive a release train. Agencies managing multiple clients need history, comparison, and alert routing on the same URLs every week. That is the same operational model as &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/pagespeed-insights-vs-automated-monitoring-when-manual-checks-arent-enough&quot;&gt;PageSpeed Insights vs automated monitoring&lt;/a&gt;: scheduled lab runs, stored results, budgets, and digests when thresholds breach.&lt;/p&gt;&lt;p&gt;Apogee Watcher is built for that portfolio pattern: organisations per agency, sites per client, discovery and manual URL lists, mobile and desktop strategies, and email when lab metrics cross budgets. We do not score &quot;ChatGPT visibility.&quot; We tell you when priority URLs slow down, error out, or drift on vitals after the deploy that might affect fetchability. Layer GEO vendors when you need prompt-level citation analytics; use monitoring when you need proof that the URLs cited in sales and docs still load. Measure what you can fix this week (access, speed, stability on named URLs), then add GEO tooling when the retainer includes citation tracking and someone owns the prompt library.&lt;/p&gt;&lt;h2 id=&quot;when-should-agencies-add-geo-or-ai-visibility-saas&quot; class=&quot;scroll-mt-24&quot;&gt;When should agencies add GEO or AI visibility SaaS?&lt;/h2&gt;&lt;p&gt;Add a dedicated GEO platform when:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Leadership expects monthly citation charts in QBRs.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Content strategy targets LLM mentions for specific buyer personas.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;You have staff to maintain contextual prompts, not only generic category queries.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Deterministic baselines are already green enough that slow URLs are not the obvious bottleneck.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Defer GEO procurement when:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;The client has not agreed bot policy or priority URLs.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Homepage-only monitoring hides checkout and docs problems.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The ask is purely &quot;make us show up in ChatGPT&quot; with no content or technical owner.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The upgrade path is sequential: fix fetchability and monitoring scope, then buy citation analytics, not the reverse.&lt;/p&gt;&lt;h2 class=&quot;scroll-mt-24&quot;&gt;FAQ&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Can we guarantee visibility in ChatGPT if Core Web Vitals are green?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;No. Green lab vitals indicate priority URLs are likely fetchable and parseable under test conditions. Citation depends on content, authority, relevance, and model behaviour. Treat CWV and speed as necessary technical hygiene, not a guarantee of mentions.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Should clients block GPTBot?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;That is a policy decision, not a performance trick. Some brands opt out of training crawlers; others allow fetch for retrieval products. Document the choice in the client file and align monitoring with URLs you intend to be public and reachable.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;What is the difference between AI search visibility and AI Overviews SEO?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;AI search visibility often means citations or mentions in LLM or AI search interfaces. AI Overviews SEO usually means how Google's AI-generated summaries affect clicks and visibility in traditional search. Overlap exists, but measurement tools and GSC reporting differ. See our &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/blog/ai-overviews-are-killing-clicks-what-the-data-shows-and-how-to-respond&quot;&gt;AI Overviews impact post&lt;/a&gt; for CTR context.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Do agencies need a GEO tool on day one?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Not always. Many engagements can start with bot policy, priority URLs, lab baselines, and scheduled monitoring alone. Add GEO when citation reporting is contracted and someone maintains prompts with buyer context, not only generic category queries.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;How many URLs should we monitor for AI visibility?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Start with ten to twenty priority URLs per client: homepage, pricing, key product or service templates, and docs paths that support sales. Expand when alert triage stays manageable. If every URL triggers alerts, the list is too wide for a first engagement.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;How does this relate to E-E-A-T or content quality?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Content quality still drives citation and ranking. Technical monitoring ensures the URLs that carry that content remain reachable after deploys. Both layers belong in a mature programme; neither replaces the other.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Can Apogee Watcher replace a GEO platform?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;No. Watcher schedules PageSpeed lab tests, tracks vitals and budgets, and alerts on regressions across client sites. GEO platforms track prompt-level citations. Use both when the retainer covers technical performance and citation reporting.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;When a client asks if they are visible in ChatGPT, answer in layers: what citation tools can track over time, and what you can prove about fetchability and speed on the URLs that matter this week. That split keeps the agency credible, gives engineering clear work, and leaves room to add GEO analytics when the scope and budget justify it. Start with deterministic baselines; add probabilistic tracking when the retainer calls for it.&lt;/p&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/sign-up&quot;&gt;Start a free Apogee Watcher account&lt;/a&gt; to schedule mobile and desktop tests with vitals budgets across client sites, or run a &lt;a rel=&quot;nofollow&quot; href=&quot;https://apogeewatcher.com/check&quot;&gt;free domain PageSpeed check&lt;/a&gt; before the first AI visibility readout.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22114/are-we-visible-in-chatgpt-what-agencies-can-measure-first</guid>
<pubDate>Wed, 08 Jul 2026 13:17:35 +0000</pubDate>
</item>
<item>
<title>Taming the Infinity: The Fast Track to PI</title>
<link>https://coderlegion.com/20130/taming-the-infinity-the-fast-track-to-pi</link>
<description>&lt;p&gt;Calculating (Pi) is one of mathematics’ oldest obsessions. In today’s digital landscape, the quest isn’t just for accuracy; it's for raw, unadulterated speed. While many know PI as 3.14159..., modern web development and computing require ways to access its value instantly, whether for high-precision scientific visualization or optimized geometric rendering. The fundamental question is: how do we get the most precise value, the fastest way possible?&lt;/p&gt;
&lt;p&gt;First, how do we make it happen? We don’t try to compute trillions of digits every time we load a webpage. Instead, the fastest way to &quot;get&quot; PI is to already have it. This is achieved through efficient, hardcoded constants stored in high-performance libraries. However, when we need extreme precision on demand, we don't use old methods like polygons; we rely on rapidly convergent series.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The modern roadmap for ultra-fast PI generation involves specific trigger points:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Employ rapidly convergent formulas: The gold standard is the Chudnovsky algorithm, which provides approximately 14 new digits of precision with every single term calculated.&lt;/p&gt;
&lt;p&gt;Utilize pre-computed constants: For 99% of web development needs, we leverage built-in constants like Math.PI in JavaScript, which are instantly available at runtime with double-precision accuracy.&lt;/p&gt;
&lt;p&gt;Hardcode beyond double-precision: When precision beyond 15-17 digits is needed for specialized apps, we store PI as a static, pre-computed string constant in our source code.&lt;/p&gt;
&lt;p&gt;This path, while appearing straightforward, has its obstacles. The problems with high-precision calculation arise the moment you demand more digits than your standard hardware can handle.&lt;/p&gt;
&lt;p&gt;Standard precision bottlenecks: Default data types (like double or float) cap out around 15–17 digits. Demanding 1,000 digits results in silent truncation.&lt;/p&gt;
&lt;p&gt;Exponential computational cost: Older algorithms get exponentially slower as you ask for more digits, paralyzing user interfaces.&lt;/p&gt;
&lt;p&gt;Memory exhaustion: Calculating billions or trillions of digits is no longer a CPU problem; it becomes a massive memory and storage bottleneck, as seen in world-record attempts.&lt;/p&gt;
&lt;p&gt;So, how do we solve these problems in our current situations? Our approach to web development must adapt based on the required speed versus precision.&lt;/p&gt;
&lt;p&gt;To overcome precision bottlenecks, we implement an abstraction layer. For applications requiring massive digits, we bypass native data types entirely and implement Custom Big Number libraries (like bignumber.js or decimal.js), which handle arithmetic on numbers represented as strings.&lt;/p&gt;
&lt;p&gt;We tackle computational cost through hybrid architecture. Our client-side applications rarely do heavy lifting. If an ultra-precise PI calculation is required, we offload the task to powerful backend services optimized for mathematical computation, which can return the pre-computed digits instantly via API.&lt;/p&gt;
&lt;p&gt;Finally, we prevent UI lockups by treating high-precision math as an asynchronous operation. Whether using Web Workers to run calculations in a separate thread on the client side, or fetching backend data asynchronously, we ensure the user interface remains responsive, even while handling complex mathematical demands.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sumita &lt;br&gt;
Web Developer&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20130/taming-the-infinity-the-fast-track-to-pi</guid>
<pubDate>Wed, 10 Jun 2026 05:38:44 +0000</pubDate>
</item>
<item>
<title>What ChatGPT Gets Wrong When You Ask It to Audit Your SEO</title>
<link>https://coderlegion.com/18939/what-chatgpt-gets-wrong-when-you-ask-it-to-audit-your-seo</link>
<description>&lt;p&gt;&lt;em&gt;We ran the experiment on our own site. The audit looked thorough. The math doesn't survive a 60-second fact check.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We wanted to know how reliable ChatGPT is as an SEO auditor, so we asked it to audit our own agency site, axiondeepdigital.com. Four prompts. Fresh thread. No setup, no priming. We then verified every concrete claim against the actual page, against PageSpeed Insights, and against our own SEO workbench.&lt;/p&gt;
&lt;p&gt;The audit came back at roughly 2,000 words. It included a numeric grade rubric (&quot;Technical SEO 9/10, Architecture 8.8/10&quot;), a prioritized fix list, named architectural risks, and an opinion on Googlebot rendering behavior. It looked like the kind of audit a senior SEO consultant might produce after a real review.&lt;/p&gt;
&lt;p&gt;Most of it was confabulation dressed as analysis. One of its predictions was exactly backwards. And in the same thread, ChatGPT contradicted itself in a way neither it nor a casual reader would catch.&lt;/p&gt;
&lt;p&gt;Here's the receipt for each prompt.&lt;/p&gt;
&lt;h3 id=&quot;prompt-1-the-grade-rubric-was-assigned-without-measurement&quot;&gt;Prompt 1: The grade rubric was assigned without measurement&lt;/h3&gt;
&lt;p&gt;We asked: &quot;Can you do a full technical SEO audit of [our URL]? I want specific issues, not a generic checklist.&quot;&lt;/p&gt;
&lt;p&gt;ChatGPT returned an extensive audit with a grade rubric:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Technical SEO: 9/10&lt;/li&gt;
&lt;li&gt;Architecture: 8.8/10&lt;/li&gt;
&lt;li&gt;Content Depth: 6.8/10&lt;/li&gt;
&lt;li&gt;Topical Authority: 6.5/10&lt;/li&gt;
&lt;li&gt;Local SEO Structure: 8.5/10&lt;/li&gt;
&lt;li&gt;Conversion SEO: 8.7/10&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These numbers feel authoritative. They map to nothing measurable. Lighthouse outputs a Performance score and a separate SEO score. Search Console reports impressions, clicks, and CTR. None of these tools produce a &quot;Technical SEO&quot; or &quot;Architecture&quot; or &quot;Conversion SEO&quot; number. The grades exist nowhere in any real auditing tool. ChatGPT invented the rubric and assigned scores against it.&lt;/p&gt;
&lt;p&gt;We then checked the factual claims. ChatGPT identified eight technology brands as &quot;mentioned&quot; on our homepage:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Vercel, Stripe, Netflix, Next.js, React, FastAPI, OpenAI, AWS&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Of those eight, three (FastAPI, OpenAI, AWS) are not mentioned on our homepage at all. That is a 37.5% hallucination rate on a claim that takes less than a minute to verify in a browser.&lt;/p&gt;
&lt;p&gt;Same pattern with the city pages it cited. It listed four geo pages by URL: /albuquerque, /las-cruces, /new-mexico, /web-development-new-mexico. The first two exist. The last two don't.&lt;/p&gt;
&lt;p&gt;When ChatGPT mixes real observations with confabulated ones inside the same paragraph, the result is harder to catch than pure fabrication. A reader assumes the whole list was verified the same way.&lt;/p&gt;
&lt;h3 id=&quot;prompt-2-chatgpt-contradicted-itself-two-messages-later&quot;&gt;Prompt 2: ChatGPT contradicted itself two messages later&lt;/h3&gt;
&lt;p&gt;We asked: &quot;What are the current Core Web Vitals (LCP, CLS, INP) for [URL] on mobile? Give me the actual numbers.&quot;&lt;/p&gt;
&lt;p&gt;ChatGPT replied:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I cannot reliably give you the real current field numbers for LCP, CLS, and INP from Google's CrUX dataset without direct access to PageSpeed Insights or the Chrome UX Report API for that URL. The web search environment here can access documentation and indexed pages, but it cannot execute a live Lighthouse/PageSpeed test against your domain or pull the live CrUX metrics feed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is correct. But ChatGPT had just spent 2,000 words in the previous message grading our site as if it had run Lighthouse against it. If it cannot execute a Lighthouse test, the grades in prompt 1 came from somewhere other than measurement. The two responses contradict each other inside the same conversation. ChatGPT did not flag the contradiction. A casual reader wouldn't either.&lt;/p&gt;
&lt;p&gt;Then it guessed anyway:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I would expect CLS to already be very good, LCP to be decent but hero/media dependent, INP to likely be your weakest metric if third party scripts or animations are present.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We ran the actual Lighthouse mobile audit the same day. Lighthouse weights each Core Web Vital and reports a contribution out of a maximum. Here is the prediction next to the measurement:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Metric &lt;/th&gt;&lt;th&gt;ChatGPT predicted &lt;/th&gt;&lt;th&gt;Lighthouse measured (mobile)&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;CLS &lt;/td&gt;&lt;td&gt;&quot;already very good&quot; &lt;/td&gt;&lt;td&gt;+25 / 25 (perfect)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;LCP &lt;/td&gt;&lt;td&gt;&quot;decent but hero/media dependent&quot; &lt;/td&gt;&lt;td&gt;&lt;strong&gt;+2 / 25&lt;/strong&gt; (lowest tier)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;INP / TBT (interaction) &lt;/td&gt;&lt;td&gt;&quot;likely your weakest metric&quot; &lt;/td&gt;&lt;td&gt;+27 / 30 (one of our best)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;CLS was a gimme. CLS is good on most modern sites and predicting that requires no information at all. LCP, the metric ChatGPT softened with &quot;decent,&quot; scored 2 out of a possible 25 on mobile, which is the bottom tier Lighthouse reports. The metric ChatGPT confidently named as weakest scored 27 out of 30, the strongest of the three. The ranking was exactly inverted.&lt;/p&gt;
&lt;p&gt;There is one more thing in this answer worth flagging. ChatGPT recommended PageSpeed Insights to retrieve the real CrUX field data. We checked. Our site does not have CrUX field data at all. Sites below Google's traffic threshold do not get field data, and the CrUX dataset returns &quot;does not have sufficient real-world speed data&quot; for our origin. A reader following ChatGPT's advice would arrive at PageSpeed, look for the field numbers ChatGPT promised, find nothing, and have no explanation for why.&lt;/p&gt;
&lt;h3 id=&quot;prompt-3-a-confident-render-analysis-without-rendering-anything&quot;&gt;Prompt 3: A confident render analysis without rendering anything&lt;/h3&gt;
&lt;p&gt;We asked: &quot;Does Google see the same content on [URL] that a human visitor sees? Walk me through what Googlebot renders vs what the user renders.&quot;&lt;/p&gt;
&lt;p&gt;ChatGPT delivered a careful, technically literate answer:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Based on the rendered structure, Googlebot is seeing substantially the same primary content that human users see on axiondeepdigital.com.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The phrase &quot;based on the rendered structure&quot; is doing all the work in that sentence. ChatGPT did not render anything. It cannot render anything. Its earlier message in this same conversation said so explicitly. The entire walkthrough of what Googlebot sees vs. what the user sees is pattern-matching on what a &quot;modern Next.js site&quot; typically does, not measurement of what our site actually does.&lt;/p&gt;
&lt;p&gt;If we had a broken hydration path, if our hero was client-only, if our city pages were rendering empty divs to the bot, the answer would have looked identical. That is the worst possible failure mode for an auditing tool: confident output that is invariant to the actual state of the thing being audited.&lt;/p&gt;
&lt;h3 id=&quot;prompt-4-three-priorities-that-have-nothing-to-do-with-the-actual-problem&quot;&gt;Prompt 4: Three priorities that have nothing to do with the actual problem&lt;/h3&gt;
&lt;p&gt;We asked: &quot;If I could only fix three things on [URL] this week to improve search rankings, what should they be and why those three?&quot;&lt;/p&gt;
&lt;p&gt;ChatGPT's answer: build a topical authority cluster, convert portfolio items into standalone case studies, improve internal semantic linking. Each recommendation came with an explanation that would apply word-for-word to any well-built Next.js agency site on the planet.&lt;/p&gt;
&lt;p&gt;What the answer did not mention: our mobile Performance score is 69, our mobile LCP scores 2 out of 25, and Google has been weighting Core Web Vitals into ranking since 2021. A real &quot;top three this week&quot; list, given the data, starts with &quot;fix mobile LCP.&quot; ChatGPT could not start there because it did not know.&lt;/p&gt;
&lt;p&gt;It also credited us with &quot;fast rendering stack using Next.js&quot; as a strength in prompt 1. On mobile, our Performance score is 69 with LCP scoring 2 of a possible 25. Fast is generous.&lt;/p&gt;
&lt;h3 id=&quot;the-deeper-problem&quot;&gt;The deeper problem&lt;/h3&gt;
&lt;p&gt;Our SEO category in Lighthouse scores 100. Best Practices is 100. Our own SEO workbench gives the site 96/100 overall. ChatGPT's gut call of &quot;9/10 Technical SEO&quot; is, by accident, close to the truth.&lt;/p&gt;
&lt;p&gt;That is the trap.&lt;/p&gt;
&lt;p&gt;ChatGPT arrived at a roughly correct grade not by measuring our site, but by pattern-matching what a well-built Next.js agency site usually looks like. We happen to be one. If we had been one of the 96.9% of small business sites our 292-site study found failing mobile Core Web Vitals, the audit would have read identically. Same grades. Same praise for &quot;fast rendering stack.&quot; Same &quot;your bottleneck is authority, not technical.&quot; Same generic top three.&lt;/p&gt;
&lt;p&gt;You cannot use ChatGPT to find out whether your site is broken, because ChatGPT will tell you it is fine either way.&lt;/p&gt;
&lt;h3 id=&quot;what-to-use-instead&quot;&gt;What to use instead&lt;/h3&gt;
&lt;p&gt;For SEO auditing, use tools that actually load your page in a real browser:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PageSpeed Insights:&lt;/strong&gt; synthetic Lighthouse run, free, gives you real numbers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Search Console URL Inspection:&lt;/strong&gt; shows you the rendered HTML Google sees.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DeepAudit AI:&lt;/strong&gt; our free real browser SEO audit, 100+ checks, full Chromium rendering. Built precisely because pattern-matching auditors lie. &lt;a rel=&quot;nofollow&quot; href=&quot;https://axiondeepdigital.com/free-seo-audit&quot;&gt;axiondeepdigital.com/free-seo-audit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ChatGPT is excellent at writing, summarizing, and explaining concepts. It is not an auditor. Asking it to audit a website is asking it to do a job it cannot do, and the danger is not that it refuses, but that it answers anyway.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a rel=&quot;nofollow&quot; href=&quot;https://axiondeepdigital.com/blog/what-chatgpt-gets-wrong-seo-audit&quot;&gt;axiondeepdigital.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18939/what-chatgpt-gets-wrong-when-you-ask-it-to-audit-your-seo</guid>
<pubDate>Tue, 26 May 2026 01:44:20 +0000</pubDate>
</item>
<item>
<title>Spring Boot File Upload Performance: Why Your API Gets Slow Under Load (And How to Fix It)</title>
<link>https://coderlegion.com/18484/spring-boot-file-upload-performance-why-your-api-gets-slow-under-load-and-how-to-fix-it</link>
<description>&lt;p&gt;Backend APIs often feel fast during development.&lt;/p&gt;
&lt;p&gt;You upload a few files locally.&lt;/p&gt;
&lt;p&gt;Everything responds instantly.&lt;/p&gt;
&lt;p&gt;Latency looks great.&lt;/p&gt;
&lt;p&gt;Memory looks stable.&lt;/p&gt;
&lt;p&gt;CPU barely moves.&lt;/p&gt;
&lt;p&gt;Then production traffic arrives.&lt;/p&gt;
&lt;p&gt;Suddenly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;uploads slow down&lt;/li&gt;
&lt;li&gt;downloads start hanging&lt;/li&gt;
&lt;li&gt;videos become painfully slow&lt;/li&gt;
&lt;li&gt;latency spikes&lt;/li&gt;
&lt;li&gt;users complain&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This happens because local testing rarely reflects real-world load.&lt;/p&gt;
&lt;p&gt;A file upload API is not just handling HTTP requests.&lt;/p&gt;
&lt;p&gt;It is dealing with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;disk I/O&lt;/li&gt;
&lt;li&gt;file buffering&lt;/li&gt;
&lt;li&gt;network transfer&lt;/li&gt;
&lt;li&gt;JVM memory pressure&lt;/li&gt;
&lt;li&gt;concurrent request handling&lt;/li&gt;
&lt;li&gt;authentication overhead&lt;/li&gt;
&lt;li&gt;metadata queries&lt;/li&gt;
&lt;li&gt;file streaming&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Under enough load, something breaks first.&lt;/p&gt;
&lt;p&gt;The real question is:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What breaks first in a Spring Boot file upload system?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I ran a benchmark to find out.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;quick-answer&quot;&gt;Quick Answer&lt;/h2&gt;
&lt;p&gt;Spring Boot handled the application layer surprisingly well under heavy load.&lt;/p&gt;
&lt;p&gt;The actual bottleneck was not request handling.&lt;/p&gt;
&lt;p&gt;It was large file delivery.&lt;/p&gt;
&lt;p&gt;Small files stayed fast.&lt;/p&gt;
&lt;p&gt;Large video responses caused latency to rise sharply.&lt;/p&gt;
&lt;p&gt;The lesson:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Spring Boot is fine for API orchestration, but direct media delivery becomes a scaling problem much faster than most developers expect.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-test-scenario&quot;&gt;The Test Scenario&lt;/h2&gt;
&lt;p&gt;The benchmark used a Spring Boot file service with typical production-style endpoints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;file upload&lt;/li&gt;
&lt;li&gt;file download&lt;/li&gt;
&lt;li&gt;metadata lookup&lt;/li&gt;
&lt;li&gt;file listing&lt;/li&gt;
&lt;li&gt;delete operations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The workload intentionally mixed different file behaviors because real systems rarely handle only one request type.&lt;/p&gt;
&lt;p&gt;The goal was not to create a synthetic benchmark that looks impressive.&lt;/p&gt;
&lt;p&gt;The goal was realism.&lt;/p&gt;
&lt;p&gt;Traffic gradually increased in stages until the system started showing visible degradation.&lt;/p&gt;
&lt;p&gt;Metrics tracked included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;request throughput&lt;/li&gt;
&lt;li&gt;P95 latency&lt;/li&gt;
&lt;li&gt;CPU usage&lt;/li&gt;
&lt;li&gt;memory usage&lt;/li&gt;
&lt;li&gt;endpoint behavior&lt;/li&gt;
&lt;li&gt;file-type response performance&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-file-upload-apis-fail-earlier-than-expected&quot;&gt;Why File Upload APIs Fail Earlier Than Expected&lt;/h2&gt;
&lt;p&gt;A standard CRUD API is relatively cheap.&lt;/p&gt;
&lt;p&gt;A file API is not.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;p&gt;A metadata lookup:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;GET /api/files/metadata/123
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;might hit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;authentication filter&lt;/li&gt;
&lt;li&gt;database query&lt;/li&gt;
&lt;li&gt;JSON serialization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is manageable.&lt;/p&gt;
&lt;p&gt;Now compare that with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;GET /api/files/download/huge-video.mp4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the system may need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;authenticate request&lt;/li&gt;
&lt;li&gt;validate permissions&lt;/li&gt;
&lt;li&gt;locate file on disk&lt;/li&gt;
&lt;li&gt;open file stream&lt;/li&gt;
&lt;li&gt;allocate buffers&lt;/li&gt;
&lt;li&gt;push large chunks over network&lt;/li&gt;
&lt;li&gt;keep thread occupied longer&lt;/li&gt;
&lt;li&gt;handle slow client reads&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That changes everything.&lt;/p&gt;
&lt;p&gt;The API layer is no longer the only concern.&lt;/p&gt;
&lt;p&gt;Your infrastructure becomes part of request latency.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;benchmark-summary&quot;&gt;Benchmark Summary&lt;/h2&gt;
&lt;p&gt;Here’s the simplified outcome.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Metric           &lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Total Requests   &lt;/td&gt;&lt;td&gt;134,000+&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Peak Load        &lt;/td&gt;&lt;td&gt;7,500 RPM&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Peak P95 Latency &lt;/td&gt;&lt;td&gt;~1.8 seconds&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;HTTP Failures    &lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Average CPU      &lt;/td&gt;&lt;td&gt;~35%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Main Bottleneck  &lt;/td&gt;&lt;td&gt;Large MP4 downloads&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;At first glance, this looks strong.&lt;/p&gt;
&lt;p&gt;Zero failures under that volume is respectable.&lt;/p&gt;
&lt;p&gt;But raw survival is not the same as healthy performance.&lt;/p&gt;
&lt;p&gt;Latency tells the real story.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=4087669585729204340&quot; alt=&quot;Spring Boot file upload latency under 7500 RPM load test&quot;&gt;&lt;br&gt;
&lt;em&gt;P95 latency stayed stable until higher RPM levels, then degraded rapidly after ~6,300 RPM.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;latency-looked-fine-until-it-didn-t&quot;&gt;Latency Looked Fine... Until It Didn’t&lt;/h2&gt;
&lt;p&gt;At lower traffic levels, performance was stable.&lt;/p&gt;
&lt;p&gt;The backend remained responsive.&lt;/p&gt;
&lt;p&gt;P95 latency stayed in a healthy range.&lt;/p&gt;
&lt;p&gt;Then the ramp increased.&lt;/p&gt;
&lt;p&gt;Past a certain threshold, latency began climbing rapidly.&lt;/p&gt;
&lt;p&gt;This pattern matters.&lt;/p&gt;
&lt;p&gt;Gradual degradation usually means the system is saturating naturally.&lt;/p&gt;
&lt;p&gt;Sudden collapse usually means architectural failure.&lt;/p&gt;
&lt;p&gt;This benchmark showed degradation, not collapse.&lt;/p&gt;
&lt;p&gt;That is actually a good sign.&lt;/p&gt;
&lt;p&gt;Still, once latency crosses production tolerance, users do not care whether your system technically survived.&lt;/p&gt;
&lt;p&gt;They only see slowness.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=13056611110152726505&quot; alt=&quot;Spring Boot file upload throughput under increasing load&quot;&gt;&lt;br&gt;
&lt;em&gt;Throughput tracked closely with target RPM until upper load levels where the system started falling behind.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;throughput-was-stable&quot;&gt;Throughput Was Stable&lt;/h2&gt;
&lt;p&gt;One interesting finding:&lt;/p&gt;
&lt;p&gt;The backend kept serving requests consistently even under heavier load.&lt;/p&gt;
&lt;p&gt;That suggests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;request routing was stable&lt;/li&gt;
&lt;li&gt;controllers were fine&lt;/li&gt;
&lt;li&gt;business logic held up&lt;/li&gt;
&lt;li&gt;auth overhead was manageable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is important because many developers blame Spring Boot too early.&lt;/p&gt;
&lt;p&gt;The framework itself was not the issue here.&lt;/p&gt;
&lt;p&gt;The architecture was.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-real-bottleneck&quot;&gt;The Real Bottleneck&lt;/h2&gt;
&lt;p&gt;This was the most useful finding.&lt;/p&gt;
&lt;p&gt;The slowest operations were not:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;uploads&lt;/li&gt;
&lt;li&gt;metadata APIs&lt;/li&gt;
&lt;li&gt;listing APIs&lt;/li&gt;
&lt;li&gt;delete operations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The worst offender was:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;large MP4 delivery&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=16639872318609390032&quot; alt=&quot;Spring Boot file upload latency comparison for PDF PNG and MP4 files&quot;&gt;&lt;br&gt;
&lt;em&gt;MP4 downloads had dramatically higher P95 latency compared to PDF and PNG files.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Small files performed dramatically better.&lt;/p&gt;
&lt;p&gt;Typical behavior looked like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;File Type &lt;/th&gt;&lt;th&gt;Approx Performance&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;PDF       &lt;/td&gt;&lt;td&gt;Extremely fast&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;PNG       &lt;/td&gt;&lt;td&gt;Moderate&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;MP4       &lt;/td&gt;&lt;td&gt;Much slower&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This makes perfect sense.&lt;/p&gt;
&lt;p&gt;Large media files create pressure across the entire stack.&lt;/p&gt;
&lt;p&gt;Including:&lt;/p&gt;
&lt;h4 id=&quot;disk-i-o&quot;&gt;Disk I/O&lt;/h4&gt;
&lt;p&gt;Reading large files repeatedly creates storage pressure.&lt;/p&gt;
&lt;p&gt;Local disk works for development.&lt;/p&gt;
&lt;p&gt;At scale, it becomes painful.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;network-throughput&quot;&gt;Network Throughput&lt;/h4&gt;
&lt;p&gt;A large file means longer response duration.&lt;/p&gt;
&lt;p&gt;More active connections.&lt;/p&gt;
&lt;p&gt;More bandwidth consumption.&lt;/p&gt;
&lt;p&gt;Slow clients make this worse.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;jvm-memory-pressure&quot;&gt;JVM Memory Pressure&lt;/h4&gt;
&lt;p&gt;Streaming often introduces buffering overhead.&lt;/p&gt;
&lt;p&gt;Even when efficient, sustained concurrency adds pressure.&lt;/p&gt;
&lt;p&gt;Memory growth is expected.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;thread-occupancy&quot;&gt;Thread Occupancy&lt;/h4&gt;
&lt;p&gt;Traditional request handling keeps resources busy longer.&lt;/p&gt;
&lt;p&gt;A quick metadata request finishes fast.&lt;/p&gt;
&lt;p&gt;A multi-second video stream does not.&lt;/p&gt;
&lt;p&gt;That reduces effective throughput.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;cpu-was-not-the-problem&quot;&gt;CPU Was Not the Problem&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=7486188393474424770&quot; alt=&quot;Spring Boot load test CPU usage chart&quot;&gt;&lt;br&gt;
&lt;em&gt;CPU usage increased gradually with traffic but remained relatively controlled during the test.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A common assumption:&lt;/p&gt;
&lt;p&gt;&quot;High latency means CPU saturation.&quot;&lt;/p&gt;
&lt;p&gt;Not here.&lt;/p&gt;
&lt;p&gt;CPU stayed relatively controlled.&lt;/p&gt;
&lt;p&gt;Average utilization hovered around the mid-thirties.&lt;/p&gt;
&lt;p&gt;That means compute was not the main bottleneck.&lt;/p&gt;
&lt;p&gt;This is useful because it changes optimization priorities.&lt;/p&gt;
&lt;p&gt;If CPU is not the problem:&lt;/p&gt;
&lt;p&gt;Do not waste time micro-optimizing controller code first.&lt;/p&gt;
&lt;p&gt;Fix architecture first.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;memory-behavior&quot;&gt;Memory Behavior&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=12873337120441916599&quot; alt=&quot;Spring Boot load test memory usage chart&quot;&gt;&lt;br&gt;
&lt;em&gt;Memory usage increased under higher throughput but stayed within a manageable range.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Memory climbed during heavier traffic.&lt;/p&gt;
&lt;p&gt;That is expected.&lt;/p&gt;
&lt;p&gt;Large streaming workloads create pressure.&lt;/p&gt;
&lt;p&gt;But the test did not suggest catastrophic memory behavior.&lt;/p&gt;
&lt;p&gt;The bigger issue remained delivery mechanics, not memory collapse.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-this-actually-means-for-production&quot;&gt;What This Actually Means for Production&lt;/h2&gt;
&lt;p&gt;This is where many teams make a bad decision.&lt;/p&gt;
&lt;p&gt;They see a working local file upload implementation and assume:&lt;/p&gt;
&lt;p&gt;&quot;Great, we can scale this.&quot;&lt;/p&gt;
&lt;p&gt;Maybe.&lt;/p&gt;
&lt;p&gt;But probably not for media-heavy workloads.&lt;/p&gt;
&lt;p&gt;A better architecture separates responsibilities.&lt;/p&gt;
&lt;p&gt;Spring Boot should handle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;authorization&lt;/li&gt;
&lt;li&gt;business rules&lt;/li&gt;
&lt;li&gt;metadata&lt;/li&gt;
&lt;li&gt;upload coordination&lt;/li&gt;
&lt;li&gt;signed URL generation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dedicated infrastructure should handle media delivery.&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS S3&lt;/li&gt;
&lt;li&gt;Cloudflare R2&lt;/li&gt;
&lt;li&gt;object storage&lt;/li&gt;
&lt;li&gt;CDN edge delivery&lt;/li&gt;
&lt;li&gt;Nginx static serving&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This dramatically reduces backend pressure.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;better-architecture-example&quot;&gt;Better Architecture Example&lt;/h2&gt;
&lt;p&gt;Instead of this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Client
   ↓
Spring Boot
   ↓
Local file disk
   ↓
Spring Boot streams file
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prefer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Client
   ↓
Spring Boot auth + metadata
   ↓
Signed URL
   ↓
S3 / R2 / CDN
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That changes scaling behavior completely.&lt;/p&gt;
&lt;p&gt;Now your application server is not acting like a media server.&lt;/p&gt;
&lt;p&gt;Which is exactly what you want.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;common-mistakes-developers-make&quot;&gt;Common Mistakes Developers Make&lt;/h2&gt;
&lt;h3 id=&quot;1-treating-upload-success-as-performance-proof&quot;&gt;1. Treating Upload Success As Performance Proof&lt;/h3&gt;
&lt;p&gt;A few successful uploads prove almost nothing.&lt;/p&gt;
&lt;p&gt;Load changes everything.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-serving-large-media-directly-forever&quot;&gt;2. Serving Large Media Directly Forever&lt;/h3&gt;
&lt;p&gt;Works early.&lt;/p&gt;
&lt;p&gt;Hurts later.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-blaming-spring-boot-too-quickly&quot;&gt;3. Blaming Spring Boot Too Quickly&lt;/h3&gt;
&lt;p&gt;The framework is often not the issue.&lt;/p&gt;
&lt;p&gt;Bad delivery architecture usually is.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-ignoring-p95-latency&quot;&gt;4. Ignoring P95 Latency&lt;/h3&gt;
&lt;p&gt;Average latency hides pain.&lt;/p&gt;
&lt;p&gt;P95 exposes real user experience.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;when-spring-boot-is-absolutely-fine&quot;&gt;When Spring Boot Is Absolutely Fine&lt;/h2&gt;
&lt;p&gt;Spring Boot works well if your workload is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;document uploads&lt;/li&gt;
&lt;li&gt;moderate internal tools&lt;/li&gt;
&lt;li&gt;admin dashboards&lt;/li&gt;
&lt;li&gt;metadata-heavy systems&lt;/li&gt;
&lt;li&gt;authenticated business APIs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your workload becomes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;video-heavy&lt;/li&gt;
&lt;li&gt;media streaming&lt;/li&gt;
&lt;li&gt;public downloads at scale&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;your architecture needs to evolve.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;practical-scaling-advice&quot;&gt;Practical Scaling Advice&lt;/h2&gt;
&lt;p&gt;If you already have a local file-based Spring Boot system:&lt;/p&gt;
&lt;p&gt;do this first.&lt;/p&gt;
&lt;h4 id=&quot;move-storage&quot;&gt;Move Storage&lt;/h4&gt;
&lt;p&gt;Shift from local disk to object storage.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;add-cdn-delivery&quot;&gt;Add CDN Delivery&lt;/h4&gt;
&lt;p&gt;Never make your app server deliver everything forever.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;use-signed-urls&quot;&gt;Use Signed URLs&lt;/h4&gt;
&lt;p&gt;Avoid routing every file request through application logic.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;benchmark-early&quot;&gt;Benchmark Early&lt;/h4&gt;
&lt;p&gt;Do not wait until users discover bottlenecks.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;watch-p95-not-just-averages&quot;&gt;Watch P95, Not Just Averages&lt;/h4&gt;
&lt;p&gt;This matters more than vanity metrics.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;how-many-uploads-can-spring-boot-handle-&quot;&gt;How many uploads can Spring Boot handle?&lt;/h3&gt;
&lt;p&gt;There is no universal number.&lt;/p&gt;
&lt;p&gt;It depends on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;file size&lt;/li&gt;
&lt;li&gt;infrastructure&lt;/li&gt;
&lt;li&gt;storage strategy&lt;/li&gt;
&lt;li&gt;concurrency&lt;/li&gt;
&lt;li&gt;request mix&lt;/li&gt;
&lt;li&gt;delivery architecture&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Small files and metadata workloads scale much better than heavy media streaming.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;should-i-serve-files-directly-from-spring-boot-&quot;&gt;Should I serve files directly from Spring Boot?&lt;/h3&gt;
&lt;p&gt;For small or internal workloads, yes.&lt;/p&gt;
&lt;p&gt;For larger public media systems, usually no.&lt;/p&gt;
&lt;p&gt;Object storage plus CDN is the better long-term design.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-do-video-downloads-slow-everything-down-&quot;&gt;Why do video downloads slow everything down?&lt;/h3&gt;
&lt;p&gt;Because they stress:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;disk reads&lt;/li&gt;
&lt;li&gt;bandwidth&lt;/li&gt;
&lt;li&gt;buffering&lt;/li&gt;
&lt;li&gt;connection duration&lt;/li&gt;
&lt;li&gt;thread occupancy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This creates systemic pressure much faster than lightweight API requests.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;final-takeaway&quot;&gt;Final Takeaway&lt;/h2&gt;
&lt;p&gt;The benchmark revealed something useful.&lt;/p&gt;
&lt;p&gt;Spring Boot did not fail.&lt;/p&gt;
&lt;p&gt;The architecture reached its natural limit.&lt;/p&gt;
&lt;p&gt;That distinction matters.&lt;/p&gt;
&lt;p&gt;If your backend handles uploads, metadata, auth, and business logic:&lt;/p&gt;
&lt;p&gt;Spring Boot is a strong fit.&lt;/p&gt;
&lt;p&gt;If your backend is also acting as a high-volume media server:&lt;/p&gt;
&lt;p&gt;you are creating your own bottleneck.&lt;/p&gt;
&lt;p&gt;Design accordingly.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally inspired by real performance benchmarking work done while building a Spring Boot file infrastructure product at BuildBaseKit.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18484/spring-boot-file-upload-performance-why-your-api-gets-slow-under-load-and-how-to-fix-it</guid>
<pubDate>Wed, 20 May 2026 18:59:08 +0000</pubDate>
</item>
<item>
<title>State of Small Business Websites (2026 Study): We Audited 292 Sites. 96.9% Fail Core Web Vitals</title>
<link>https://coderlegion.com/15676/state-of-small-business-websites-2026-study-we-audited-292-sites-96-9-fail-core-web-vitals</link>
<description>&lt;p&gt;Most performance articles repeat the same three stats everyone has already read. &quot;Amazon found a 100ms delay costs 1% revenue.&quot; &quot;53% of mobile users leave if a page takes over 3 seconds.&quot; You have seen them. They are old.&lt;/p&gt;
&lt;p&gt;Last month we ran a different experiment. We took a real-world list of 292 North American B2B prospects, pointed our headless browser audit tool at every site, and measured what actually ships to production in 2026.&lt;/p&gt;
&lt;p&gt;The short version: &lt;strong&gt;96.9% of the sites we audited fail at least one Core Web Vital on mobile&lt;/strong&gt;. Only 3.1% pass all three. And &lt;strong&gt;100% of 191 sites with valid accessibility data failed the Link Labels check&lt;/strong&gt;. Not most. Not 95%. Every single one.&lt;/p&gt;
&lt;p&gt;The full report, dataset, and methodology are open source. This post is the developer-angle on what is breaking and why.&lt;/p&gt;
&lt;p&gt;Report: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.axiondeepdigital.com/research/state-of-small-business-websites-2026&quot;&gt;axiondeepdigital.com/research/state-of-small-business-websites-2026&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;key-stats-copy-paste-friendly-&quot;&gt;Key stats (copy-paste friendly)&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Metric &lt;/th&gt;&lt;th&gt;Sample &lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Fail at least one Core Web Vital (mobile) &lt;/td&gt;&lt;td&gt;n=191 &lt;/td&gt;&lt;td&gt;&lt;strong&gt;96.9%&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pass all three Core Web Vitals (mobile) &lt;/td&gt;&lt;td&gt;n=191 &lt;/td&gt;&lt;td&gt;&lt;strong&gt;3.1%&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Fail axe-core Link Labels rule &lt;/td&gt;&lt;td&gt;n=191 &lt;/td&gt;&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Post &quot;poor&quot; mobile LCP (&amp;gt;4.0s) &lt;/td&gt;&lt;td&gt;n=191 &lt;/td&gt;&lt;td&gt;&lt;strong&gt;86.4%&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Post &quot;poor&quot; mobile FCP (&amp;gt;3.0s) &lt;/td&gt;&lt;td&gt;n=191 &lt;/td&gt;&lt;td&gt;&lt;strong&gt;72.4%&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pass mobile CLS (&amp;lt;0.1) &lt;/td&gt;&lt;td&gt;n=191 &lt;/td&gt;&lt;td&gt;&lt;strong&gt;85.5%&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Total domains scanned &lt;/td&gt;&lt;td&gt;n=292 &lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Source: State of Small Business Websites 2026, Axion Deep Digital Research. April 2026.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;how-we-measured&quot;&gt;How we measured&lt;/h3&gt;
&lt;p&gt;This study runs on our production audit infrastructure. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.axiondeepdigital.com/free-seo-audit&quot;&gt;DeepAudit AI&lt;/a&gt; is the same pipeline we run against client sites daily. The dataset below is 292 consecutive scans pulled from a single week in April 2026. Because the pipeline runs continuously, future snapshots will replicate this analysis against a larger and eventually randomized sample, and the methodology stays frozen so the comparisons hold.&lt;/p&gt;
&lt;p&gt;Each scan runs a real Chromium instance inside a Lambda (Puppeteer plus &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Sparticuz/chromium&quot;&gt;@sparticuz/chromium&lt;/a&gt;), renders the page the way Googlebot would, waits for hydration, and then measures against:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lighthouse mobile Core Web Vitals (LCP, FCP, CLS) using Google's published &lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/articles/vitals&quot;&gt;thresholds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/dequelabs/axe-core&quot;&gt;axe-core&lt;/a&gt; accessibility evaluations&lt;/li&gt;
&lt;li&gt;60+ technical SEO checks (meta, headings, schema, canonicals, alt text)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.domcop.com/openpagerank/&quot;&gt;Open PageRank&lt;/a&gt; for domain authority&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;191 of the 292 sites returned valid Lighthouse mobile data. 191 returned valid axe-core data. 260 had valid Open PageRank. Every finding below is gated on the subset of sites where the relevant measurement succeeded, not averaged over missing data.&lt;/p&gt;
&lt;h4 id=&quot;what-this-study-does-and-does-not-prove&quot;&gt;What this study does and does not prove&lt;/h4&gt;
&lt;p&gt;Before the numbers, the honest scope:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;This is a purposive sample, not a random one.&lt;/strong&gt; The 292 domains came from a North American B2B prospect list, which skews toward construction, trades, and small professional services. It is not representative of the web as a whole.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What it does represent&lt;/strong&gt; is the category of business that small digital agencies actually get hired to fix. If you pitch to this market, the sites you convert will look like this sample.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One snapshot per site.&lt;/strong&gt; Results reflect the state of each site on the day it was scanned. A site could have shipped a perf fix the next morning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lighthouse mobile scores vary.&lt;/strong&gt; Google's Lighthouse CI documentation acknowledges a ±5 point variance across runs. None of the findings below hinge on a difference smaller than that. The 96.9% failure rate and the 100% Link Labels failure are robust to that noise.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The full dataset is open. Every row, every score, every axe-core violation is downloadable from the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.axiondeepdigital.com/research/state-of-small-business-websites-2026/methodology&quot;&gt;methodology page&lt;/a&gt;. If you disagree with a finding, you can reproduce it or refute it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;finding-1-lcp-is-the-silent-killer&quot;&gt;Finding 1: LCP is the silent killer&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;86.4% of sites post poor mobile LCP. Only 5.2% are good.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Largest Contentful Paint over 4.0 seconds is Google's &quot;poor&quot; threshold. Most of the sites we measured clock in between 4 and 8 seconds on mobile. Some over 10.&lt;/p&gt;
&lt;p&gt;What is causing it, in order of frequency:&lt;/p&gt;
&lt;h4 id=&quot;1-hero-images-served-as-full-resolution-png&quot;&gt;1. Hero images served as full-resolution PNG&lt;/h4&gt;
&lt;p&gt;One site we looked at ships a 2400x1600 PNG as its hero, served at 3.2 MB. Mobile user on 4G? That is 6 seconds of transfer on a good connection. The image renders last, so LCP measures the whole download.&lt;/p&gt;
&lt;p&gt;The fix is trivial if you are on Next.js:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;import Image from 'next/image';

&amp;lt;Image
  src=&quot;/hero.jpg&quot;
  alt=&quot;...&quot;
  width={1200}
  height={600}
  priority
  sizes=&quot;(max-width: 768px) 100vw, 1200px&quot;
  quality={75}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;priority&lt;/code&gt; injects a preload hint. &lt;code&gt;sizes&lt;/code&gt; tells the browser to pick the right srcset variant for the viewport. &lt;code&gt;quality=75&lt;/code&gt; on JPEG looks identical to 90 in blind testing. Next.js serves WebP or AVIF automatically based on the Accept header.&lt;/p&gt;
&lt;p&gt;If you are not on Next.js, the same rules apply. &lt;code&gt;&amp;lt;link rel=&quot;preload&quot; as=&quot;image&quot;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; with AVIF first, and a &lt;code&gt;srcset&lt;/code&gt; + &lt;code&gt;sizes&lt;/code&gt; combo.&lt;/p&gt;
&lt;h4 id=&quot;2-render-blocking-javascript-in-the-head&quot;&gt;2. Render-blocking JavaScript in the head&lt;/h4&gt;
&lt;p&gt;Tag Manager. Hotjar. Intercom. Drift. One of every four sites we audited had three or more synchronous third-party scripts in the head. Each one blocks HTML parsing until it is fetched, parsed, and executed.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;defer&lt;/code&gt; attribute is older than most React developers. Use it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script src=&quot;/vendor/gtm.js&quot; defer&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or on Next.js:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;import Script from 'next/script';

&amp;lt;Script src=&quot;https://www.googletagmanager.com/gtm.js&quot; strategy=&quot;afterInteractive&quot; /&amp;gt;
&amp;lt;Script src=&quot;https://widget.intercom.io/widget.js&quot; strategy=&quot;lazyOnload&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;afterInteractive&lt;/code&gt; loads after hydration. &lt;code&gt;lazyOnload&lt;/code&gt; waits until the browser is idle. Neither blocks the LCP element.&lt;/p&gt;
&lt;h4 id=&quot;3-custom-fonts-with-no-font-display-directive&quot;&gt;3. Custom fonts with no &lt;code&gt;font-display&lt;/code&gt; directive&lt;/h4&gt;
&lt;p&gt;Default font-loading behavior blocks text rendering for up to 3 seconds. If your LCP element contains text, it cannot paint until the font loads.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter.woff2') format('woff2');
  font-display: swap;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;swap&lt;/code&gt; tells the browser to render with a system fallback immediately, then swap to the custom font once it arrives. On Next.js, &lt;code&gt;next/font&lt;/code&gt; does this automatically. On anything else, you write it in three lines.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;finding-2-first-contentful-paint-is-worse-than-lcp-and-nobody-talks-about-it&quot;&gt;Finding 2: First Contentful Paint is worse than LCP, and nobody talks about it&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;72.4% of sites post poor mobile FCP.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;FCP is the time to the first pixel of anything meaningful. It is the metric that correlates most directly with user perception of speed. If it takes 4 seconds to paint a single headline, the user has already decided the site is broken, regardless of how fast the rest of the page loads.&lt;/p&gt;
&lt;p&gt;The causes overlap with LCP but there is one specific pattern we saw repeatedly: &lt;strong&gt;a client-side routing framework rehydrating the whole page before rendering anything&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here is the anti-pattern. A site ships a React SPA that serves an empty shell HTML, boots up, fetches the route data, then renders. The browser sees nothing visible until JavaScript finishes executing. The fix is either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Server-side render the first paint (Next.js App Router, Remix, SvelteKit — pick one)&lt;/li&gt;
&lt;li&gt;Ship static HTML for the first paint, hydrate on demand (Astro model)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are shipping a React SPA as the entire site in 2026, you are fighting gravity. The frameworks that win on FCP are the ones that put real HTML on the first GET.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;finding-3-100-of-sites-failed-link-labels&quot;&gt;Finding 3: 100% of sites failed Link Labels&lt;/h3&gt;
&lt;p&gt;This is the number that should bother you most.&lt;/p&gt;
&lt;p&gt;axe-core's Link Labels rule checks that every &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element has a discernible accessible name. Every single one of the 191 sites we evaluated had at least one link that failed this rule. Every one.&lt;/p&gt;
&lt;p&gt;What does a failing link look like? It looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;a href=&quot;/products&quot;&amp;gt;
  &amp;lt;svg&amp;gt;...&amp;lt;/svg&amp;gt;
&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A screen reader reads &quot;link&quot; and stops. There is no text content and no &lt;code&gt;aria-label&lt;/code&gt;. The user has no idea where the link goes.&lt;/p&gt;
&lt;p&gt;The fix is one attribute:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;a href=&quot;/products&quot; aria-label=&quot;Our products&quot;&amp;gt;
  &amp;lt;svg aria-hidden=&quot;true&quot;&amp;gt;...&amp;lt;/svg&amp;gt;
&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Social icon sets are the most common offender. Every Twitter, LinkedIn, and Instagram icon in every footer. Almost every one of them was an unlabeled link. Across 191 sites, we did not find a single site that had labeled every one of its icon-only links.&lt;/p&gt;
&lt;p&gt;This is not an edge case. This is the baseline.&lt;/p&gt;
&lt;p&gt;The broader pattern is: &lt;strong&gt;developers ship components library icons without reading the a11y docs&lt;/strong&gt;. React-icons, Font Awesome, Lucide, Heroicons — all of them render SVGs inside anchors by default, and unless you explicitly add an &lt;code&gt;aria-label&lt;/code&gt; or sibling text, the resulting link is unreadable.&lt;/p&gt;
&lt;p&gt;If you ship a component with an icon-only link, wrap it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;export function SocialLink({ href, label, children }: SocialLinkProps) {
  return (
    &amp;lt;a href={href} aria-label={label}&amp;gt;
      &amp;lt;span aria-hidden=&quot;true&quot;&amp;gt;{children}&amp;lt;/span&amp;gt;
    &amp;lt;/a&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Three lines. You now pass axe-core.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;finding-4-cls-is-the-one-metric-most-sites-get-right&quot;&gt;Finding 4: CLS is the one metric most sites get right&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;85.5% of sites pass mobile CLS.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Cumulative Layout Shift is the one Core Web Vital developers have actually internalized. It helps that every modern framework bakes in &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; on images by default, and that &lt;code&gt;font-display: swap&lt;/code&gt; cuts down on font-swap shifts. The sites that fail CLS are doing something unusual: late-loading ads, &lt;code&gt;position: sticky&lt;/code&gt; headers without reserving space, or hero carousels that resize after the first slide loads.&lt;/p&gt;
&lt;p&gt;If your site passes everything else but fails CLS, the cause is almost always one of those three.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-takeaways-for-developers&quot;&gt;The takeaways for developers&lt;/h3&gt;
&lt;p&gt;If you read nothing else from this, read this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Priority-load one image and one font.&lt;/strong&gt; Your LCP element and your primary typeface. Everything else lazy-loads. This single change moves most sites from &quot;poor&quot; LCP to &quot;good.&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Defer or lazy-load every third-party script.&lt;/strong&gt; If Tag Manager is synchronous in your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, you are paying for it on every paint. Move it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run axe-core on every PR.&lt;/strong&gt; A CI check that runs &lt;code&gt;@axe-core/cli&lt;/code&gt; against your built site will catch unlabeled links before they ship. It takes 10 minutes to configure and it will save you from the baseline failure.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test on mobile, not desktop.&lt;/strong&gt; Every site we audited looked fine on desktop. 96.9% of them fail on mobile. Chrome DevTools has a &quot;Mobile&quot; preset in the Performance tab. Use it on every build.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-dataset&quot;&gt;The dataset&lt;/h3&gt;
&lt;p&gt;The full 292-site dataset, methodology, and reproducibility notes are open. If you want to cite any of these findings in a post, article, or conference talk, you can pull the raw scan JSON and reproduce every chart.&lt;/p&gt;
&lt;p&gt;Report: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.axiondeepdigital.com/research/state-of-small-business-websites-2026&quot;&gt;State of Small Business Websites 2026&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Suggested citation (BibTeX friendly):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Gutierrez, J.R. (2026). State of Small Business Websites 2026.
Axion Deep Digital Research, n=292.
axiondeepdigital.com/research/state-of-small-business-websites-2026
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you find an error, contact us and we will update the public methodology page with the correction and the reporter credit.&lt;/p&gt;
&lt;h3 id=&quot;the-takeaway-in-one-sentence&quot;&gt;The takeaway, in one sentence&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The small business web is not sophisticated enough to be slow. It is slow because nothing is prioritized, nothing is deferred, and nothing is labeled.&lt;/strong&gt; Every one of the findings above is fixable in a single afternoon by a developer who knows the four keywords: &lt;code&gt;priority&lt;/code&gt;, &lt;code&gt;defer&lt;/code&gt;, &lt;code&gt;font-display: swap&lt;/code&gt;, and &lt;code&gt;aria-label&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That is what the 3.1% do.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Further reading&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/articles/vitals&quot;&gt;Core Web Vitals thresholds&lt;/a&gt; (Google)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.chrome.com/docs/lighthouse/performance/performance-scoring&quot;&gt;Lighthouse scoring weights&lt;/a&gt; (Google)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dequeuniversity.com/rules/axe/&quot;&gt;axe-core rule descriptions&lt;/a&gt; (Deque)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://web.dev/articles/rail&quot;&gt;The RAIL performance model&lt;/a&gt; (Google)&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/15676/state-of-small-business-websites-2026-study-we-audited-292-sites-96-9-fail-core-web-vitals</guid>
<pubDate>Thu, 23 Apr 2026 20:02:01 +0000</pubDate>
</item>
<item>
<title>What a 98 Lighthouse Score Actually Takes</title>
<link>https://coderlegion.com/13788/what-a-98-lighthouse-score-actually-takes</link>
<description>&lt;p&gt;Everybody talks about Lighthouse scores, but very few sites actually achieve a high one.&lt;/p&gt;
&lt;p&gt;Across the sites we have audited at Axion Deep Digital, the average score is &lt;strong&gt;44 out of 100&lt;/strong&gt;. These are not abandoned projects. These are active businesses investing in growth.&lt;/p&gt;
&lt;p&gt;Performance is not just a technical metric. It directly impacts both ranking and conversion. A slow site ranks lower and loses users before they even engage.&lt;/p&gt;
&lt;p&gt;We rebuilt our own site with a clear goal: reach &lt;strong&gt;98+ across all Lighthouse categories on mobile&lt;/strong&gt;, under realistic conditions.&lt;/p&gt;
&lt;p&gt;Here is what that required.&lt;/p&gt;
&lt;h3 id=&quot;start-with-the-foundation&quot;&gt;Start With the Foundation&lt;/h3&gt;
&lt;p&gt;Your framework determines what is possible.&lt;/p&gt;
&lt;p&gt;We use static generation so pages are pre rendered and delivered instantly. If your stack introduces unnecessary overhead, optimization has limits.&lt;/p&gt;
&lt;h3 id=&quot;images-are-the-largest-bottleneck&quot;&gt;Images Are the Largest Bottleneck&lt;/h3&gt;
&lt;p&gt;On most sites, images account for the majority of page weight.&lt;/p&gt;
&lt;p&gt;We focus on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Modern formats like WebP and AVIF  &lt;/li&gt;
&lt;li&gt;Proper sizing based on actual display  &lt;/li&gt;
&lt;li&gt;Lazy loading below the fold  &lt;/li&gt;
&lt;li&gt;Explicit dimensions to prevent layout shift  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;fonts-add-hidden-latency&quot;&gt;Fonts Add Hidden Latency&lt;/h3&gt;
&lt;p&gt;Fonts often introduce multiple network requests before text appears.&lt;/p&gt;
&lt;p&gt;We:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Self host fonts  &lt;/li&gt;
&lt;li&gt;Subset unused characters  &lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;font-display: swap&lt;/code&gt;  &lt;/li&gt;
&lt;li&gt;Preload primary fonts  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This significantly improves perceived performance.&lt;/p&gt;
&lt;h3 id=&quot;javascript-should-be-minimal&quot;&gt;JavaScript Should Be Minimal&lt;/h3&gt;
&lt;p&gt;JavaScript must be downloaded, parsed, and executed.&lt;/p&gt;
&lt;p&gt;We:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remove unused dependencies  &lt;/li&gt;
&lt;li&gt;Split code by route and component  &lt;/li&gt;
&lt;li&gt;Defer non critical scripts  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is to reduce main thread work and improve responsiveness.&lt;/p&gt;
&lt;h3 id=&quot;css-controls-first-render&quot;&gt;CSS Controls First Render&lt;/h3&gt;
&lt;p&gt;Instead of blocking rendering with large stylesheets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inline critical CSS  &lt;/li&gt;
&lt;li&gt;Load remaining styles asynchronously  &lt;/li&gt;
&lt;li&gt;Remove unused rules  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;core-web-vitals&quot;&gt;Core Web Vitals&lt;/h3&gt;
&lt;p&gt;These are the metrics that matter most:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LCP&lt;/strong&gt; measures how fast the main content appears  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CLS&lt;/strong&gt; measures layout stability  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;INP&lt;/strong&gt; measures responsiveness  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Optimizing the system improves all three.&lt;/p&gt;
&lt;h3 id=&quot;server-configuration-still-matters&quot;&gt;Server Configuration Still Matters&lt;/h3&gt;
&lt;p&gt;Even a fast frontend can be slowed down by poor delivery.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enable Brotli compression  &lt;/li&gt;
&lt;li&gt;Configure caching correctly  &lt;/li&gt;
&lt;li&gt;Use a CDN for global delivery  &lt;/li&gt;
&lt;li&gt;Add security headers  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;accessibility-improves-everything&quot;&gt;Accessibility Improves Everything&lt;/h3&gt;
&lt;p&gt;Accessibility overlaps with SEO and usability.&lt;/p&gt;
&lt;p&gt;Common issues include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Missing alt text  &lt;/li&gt;
&lt;li&gt;Poor contrast  &lt;/li&gt;
&lt;li&gt;Unlabeled inputs  &lt;/li&gt;
&lt;li&gt;Incorrect heading structure  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;/h3&gt;
&lt;p&gt;Performance affects ranking. It also affects conversion.&lt;/p&gt;
&lt;p&gt;Testing on a fast device is misleading. Real users are on slower networks and older hardware.&lt;/p&gt;
&lt;h3 id=&quot;the-fix&quot;&gt;The Fix&lt;/h3&gt;
&lt;p&gt;We built &lt;strong&gt;DeepAudit AI&lt;/strong&gt; to evaluate sites the right way.&lt;/p&gt;
&lt;p&gt;It renders your site in a real browser and audits what actually loads.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No signup  &lt;/li&gt;
&lt;li&gt;Takes about 60 seconds  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://axiondeepdigital.com/free-seo-audit&quot;&gt;https://axiondeepdigital.com/free-seo-audit&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/13788/what-a-98-lighthouse-score-actually-takes</guid>
<pubDate>Tue, 31 Mar 2026 21:32:58 +0000</pubDate>
</item>
<item>
<title>How the Browser Rendering Pipeline Actually Works</title>
<link>https://coderlegion.com/12816/how-the-browser-rendering-pipeline-actually-works</link>
<description>&lt;p&gt;Most frontend developers spend their time inside frameworks like React, Vue, or Angular. But underneath every framework is the same system: the browser turning code into pixels on a screen.&lt;/p&gt;
&lt;p&gt;This process is known as the &lt;strong&gt;browser rendering pipeline&lt;/strong&gt; (or the &lt;strong&gt;critical rendering path&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;In simplified terms:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTML → DOM
CSS → CSSOM
DOM + CSSOM → Render Tree
Render Tree → Layout
Layout → Paint
Paint → Composite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Understanding this pipeline explains many real-world performance issues that developers encounter.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-html-parsing-dom-construction&quot;&gt;1. HTML Parsing → DOM Construction&lt;/h3&gt;
&lt;p&gt;The browser begins by parsing HTML and converting it into the &lt;strong&gt;Document Object Model (DOM)&lt;/strong&gt;, a tree structure representing the page.&lt;/p&gt;
&lt;p&gt;Example HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;body&amp;gt;
  &amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;Welcome&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DOM representation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Document
 └── body
      ├── h1
      └── p
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Browsers &lt;strong&gt;stream-parse HTML&lt;/strong&gt;, meaning they start building the DOM before the entire document is downloaded.&lt;/p&gt;
&lt;p&gt;However, JavaScript can interrupt this process. When the parser encounters a blocking script:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script src=&quot;app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DOM construction pauses while the script executes. This is why script placement or using &lt;code&gt;defer&lt;/code&gt; matters.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-css-parsing-cssom-construction&quot;&gt;2. CSS Parsing → CSSOM Construction&lt;/h3&gt;
&lt;p&gt;While the DOM represents structure, CSS defines how elements should appear.&lt;/p&gt;
&lt;p&gt;The browser parses CSS into the &lt;strong&gt;CSS Object Model (CSSOM)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Example CSS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;h1 {
  color: red;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The browser resolves:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the cascade&lt;/li&gt;
&lt;li&gt;specificity&lt;/li&gt;
&lt;li&gt;inheritance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eventually, every element receives its &lt;strong&gt;computed styles&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-dom-cssom-render-tree&quot;&gt;3. DOM + CSSOM → Render Tree&lt;/h3&gt;
&lt;p&gt;The DOM alone isn’t enough to render a page. The browser merges the DOM and CSSOM to create a &lt;strong&gt;Render Tree&lt;/strong&gt;, which contains only visible elements with their computed styles.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;p&gt;DOM:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;body
 ├─ h1
 ├─ p
 └─ script
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Render Tree:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;body
 ├─ h1 (styled)
 └─ p (styled)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Elements such as &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt;, or elements with &lt;code&gt;display: none&lt;/code&gt; are excluded.&lt;/p&gt;
&lt;p&gt;The render tree represents &lt;strong&gt;what actually needs to be drawn&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-layout-reflow-&quot;&gt;4. Layout (Reflow)&lt;/h3&gt;
&lt;p&gt;Once the render tree is built, the browser calculates the &lt;strong&gt;geometry of each element&lt;/strong&gt;. This stage is called &lt;strong&gt;layout&lt;/strong&gt; (or reflow).&lt;/p&gt;
&lt;p&gt;The browser determines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;width and height&lt;/li&gt;
&lt;li&gt;position&lt;/li&gt;
&lt;li&gt;spacing and box model calculations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;h1 → x:0 y:0 width:800 height:40
p  → x:0 y:40 width:800 height:20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Layout can be expensive because a change in one element may affect many others. For example, changing a container’s width might require recalculating layout for the entire subtree.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-paint&quot;&gt;5. Paint&lt;/h3&gt;
&lt;p&gt;After layout, the browser converts elements into &lt;strong&gt;drawing instructions&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Painting includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;text&lt;/li&gt;
&lt;li&gt;colors&lt;/li&gt;
&lt;li&gt;borders&lt;/li&gt;
&lt;li&gt;shadows&lt;/li&gt;
&lt;li&gt;images&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this stage the browser determines &lt;strong&gt;how elements should be visually drawn&lt;/strong&gt;, but pixels are not yet combined into the final frame.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-compositing&quot;&gt;6. Compositing&lt;/h3&gt;
&lt;p&gt;Modern browsers split the page into &lt;strong&gt;layers&lt;/strong&gt; and send them to the GPU compositor.&lt;/p&gt;
&lt;p&gt;Certain properties create separate layers, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;transform&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opacity&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;position: fixed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;video&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;canvas&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The GPU then &lt;strong&gt;combines these layers into the final image displayed on the screen&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This step is what enables smooth animations and efficient rendering.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-16ms-frame-budget&quot;&gt;The 16ms Frame Budget&lt;/h3&gt;
&lt;p&gt;Browsers aim for &lt;strong&gt;60 frames per second&lt;/strong&gt;, which means each frame must be processed within about &lt;strong&gt;16 milliseconds&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Within that time the browser must run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;JavaScript
Style calculation
Layout
Paint
Composite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the work exceeds this budget, the result is dropped frames and visible UI lag.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;not-all-css-changes-are-equal&quot;&gt;Not All CSS Changes Are Equal&lt;/h3&gt;
&lt;p&gt;Different CSS properties affect different stages of the pipeline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Layout-triggering properties (expensive)&lt;/strong&gt;&lt;br&gt;
Examples: &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Style → Layout → Paint → Composite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Paint-only properties&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Examples: &lt;code&gt;background-color&lt;/code&gt;, &lt;code&gt;border-color&lt;/code&gt;, &lt;code&gt;box-shadow&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Style → Paint → Composite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Composite-only properties (fastest)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Examples: &lt;code&gt;transform&lt;/code&gt;, &lt;code&gt;opacity&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Style → Composite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is why modern animation guidelines recommend using &lt;code&gt;transform&lt;/code&gt; and &lt;code&gt;opacity&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-this-matters-1&quot;&gt;Why This Matters&lt;/h3&gt;
&lt;p&gt;Many developers blame frameworks when performance issues appear. In reality, problems usually come from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;excessive DOM updates&lt;/li&gt;
&lt;li&gt;layout thrashing&lt;/li&gt;
&lt;li&gt;expensive paint operations&lt;/li&gt;
&lt;li&gt;overly complex render trees&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Frameworks may change, but browser fundamentals remain the same.&lt;/p&gt;
&lt;p&gt;Most developers think in terms of:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Components → State → UI
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But the browser ultimately thinks in:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DOM → Style → Layout → Paint → Composite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Understanding that model is what separates &lt;strong&gt;framework users from true frontend engineers&lt;/strong&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/12816/how-the-browser-rendering-pipeline-actually-works</guid>
<pubDate>Thu, 12 Mar 2026 12:53:24 +0000</pubDate>
</item>
<item>
<title>Boosting Web Performance with Image Optimization Techniques</title>
<link>https://coderlegion.com/11668/boosting-web-performance-with-image-optimization-techniques</link>
<description>&lt;p&gt;Images can make or break a web application’s performance. A well-optimized image not only improves loading speed but also enhances user experience. Here are some effective techniques for optimizing images optimized for your audience's chill vibes.&lt;/p&gt;
&lt;h4 id=&quot;choosing-the-right-formats&quot;&gt;Choosing the Right Formats&lt;/h4&gt;
&lt;p&gt;Selecting the appropriate image format is crucial. Here’s a quick breakdown:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JPEG&lt;/strong&gt;: Ideal for photos and images with gradients. Great balance between quality and file size.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PNG&lt;/strong&gt;: Best for images needing transparency. Generally larger file sizes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SVG&lt;/strong&gt;: Perfect for logos and icons. Scalability without loss of quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebP&lt;/strong&gt;: A modern format that provides superior compression without sacrificing quality. Consider using it for environments where it’s supported.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;real-world-example&quot;&gt;Real-World Example&lt;/h5&gt;
&lt;p&gt;Consider a photography portfolio. If images are in PNG format but could be JPEG, the file size may be unnecessarily large. Converting can save loading time significantly.&lt;/p&gt;
&lt;h4 id=&quot;compression-techniques&quot;&gt;Compression Techniques&lt;/h4&gt;
&lt;p&gt;Compressing images before uploading can dramatically reduce file sizes. Tools you can use include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://tinypng.com/&quot;&gt;TinyPNG&lt;/a&gt; or &lt;a rel=&quot;nofollow&quot; href=&quot;https://tinyjpg.com/&quot;&gt;TinyJPG&lt;/a&gt;&lt;/strong&gt;: Easy online tools for compressing JPEG and PNG files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ImageMagick&lt;/strong&gt;: A command-line tool effective for batch processing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;practical-tip&quot;&gt;Practical Tip&lt;/h5&gt;
&lt;p&gt;When using &lt;a rel=&quot;nofollow&quot; href=&quot;https://tinypng.com/&quot;&gt;TinyPNG&lt;/a&gt;, I saw a 70% reduction in file size without noticeable quality loss on my images. Use compression settings that prioritize quality but don’t shy away from experimenting—every ounce of byte counts!&lt;/p&gt;
&lt;h4 id=&quot;implementing-lazy-loading&quot;&gt;Implementing Lazy Loading&lt;/h4&gt;
&lt;p&gt;Instead of loading all images at once, lazy loading ensures that images are only loaded as they come into the user’s viewport. This saves bandwidth and speeds up initial loading times.&lt;/p&gt;
&lt;h5 id=&quot;simple-implementation&quot;&gt;Simple Implementation&lt;/h5&gt;
&lt;p&gt;Many libraries can help with lazy loading, but a pure JavaScript function can do the trick:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const lazyImages = document.querySelectorAll('img[data-src]');

const lazyLoad = () =&amp;gt; {
  lazyImages.forEach(img =&amp;gt; {
    if (img.getBoundingClientRect().top &amp;lt; window.innerHeight) {
      img.src = img.dataset.src;
      img.classList.remove('lazy');
    }
  });
};

window.addEventListener('scroll', lazyLoad);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure your HTML images have a &lt;code&gt;data-src&lt;/code&gt; attribute where the actual image URL will be stored, and this function will load them when necessary.&lt;/p&gt;
&lt;h4 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h4&gt;
&lt;p&gt;Optimizing images is a balance between quality and performance. Selecting the right formats, compressing wisely, and implementing lazy loading can lead to a noticeably faster web experience. After applying these techniques, watch your bounce rates decrease and engagement soar. Go ahead and give it a try, you might be surprised by the results!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/11668/boosting-web-performance-with-image-optimization-techniques</guid>
<pubDate>Sun, 15 Feb 2026 02:00:57 +0000</pubDate>
</item>
<item>
<title>Role of Web Performance in the Web Architecture</title>
<link>https://coderlegion.com/3836/role-of-web-performance-in-the-web-architecture</link>
<description>&lt;p&gt;&lt;strong&gt;Web Performance&lt;/strong&gt; is about how &lt;strong&gt;fast, responsive, and smooth&lt;/strong&gt; a website feels for the user — and it affects &lt;strong&gt;user experience, SEO, conversion rates&lt;/strong&gt;, and even costs.&lt;/p&gt;
&lt;h4 id=&quot;it-s-not-a-single-tool-it-s-the-result-of-how-well-everything-works-together-&quot;&gt;It’s not a single tool — it’s the &lt;strong&gt;result&lt;/strong&gt; of how well everything works &lt;strong&gt;together&lt;/strong&gt;.&lt;/h4&gt;
&lt;hr&gt;
&lt;h3 id=&quot;key-goals-of-web-performance&quot;&gt;&lt;strong&gt;Key Goals of Web Performance&lt;/strong&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Goal                       &lt;/th&gt;&lt;th&gt;Why It Matters&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;⏱️ Fast load times         &lt;/td&gt;&lt;td&gt;Users don’t wait → lower bounce rates&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Smooth interactivity    &lt;/td&gt;&lt;td&gt;Keeps users engaged and satisfied&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Optimized data usage    &lt;/td&gt;&lt;td&gt;Crucial for mobile and slow networks&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Fast first render       &lt;/td&gt;&lt;td&gt;Users &quot;see something&quot; ASAP&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Low time to interactive &lt;/td&gt;&lt;td&gt;Pages feel ready quickly&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;where-web-performance-fits-in-the-flow&quot;&gt;&lt;strong&gt;Where Web Performance Fits in the Flow&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Here’s how each part contributes to &lt;strong&gt;web performance&lt;/strong&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Component                        &lt;/th&gt;&lt;th&gt;Performance Role&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;DNS&lt;/strong&gt;                          &lt;/td&gt;&lt;td&gt;Faster resolution = faster initial connection&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;SSL/TLS&lt;/strong&gt;                      &lt;/td&gt;&lt;td&gt;Fast handshake = quicker secure connection&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CDN&lt;/strong&gt;                          &lt;/td&gt;&lt;td&gt;Delivers content from nearby = low latency&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Compression&lt;/strong&gt;                  &lt;/td&gt;&lt;td&gt;Smaller files = faster download &amp;amp; render&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Caching (via CDN or browser)&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Avoids re-downloading = near-instant loads&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Minified HTML/CSS/JS&lt;/strong&gt;         &lt;/td&gt;&lt;td&gt;Less code = faster processing&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Optimized Images (WebP/AVIF)&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Smaller, faster-loading media&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Browser Rendering&lt;/strong&gt;            &lt;/td&gt;&lt;td&gt;Efficient DOM/CSS = smoother display&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;measured-with-&quot;&gt;Measured With:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lighthouse&lt;/strong&gt; (Chrome DevTools)&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Web Vitals&lt;/strong&gt; (by Google):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LCP&lt;/strong&gt; (Largest Contentful Paint)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FID&lt;/strong&gt; (First Input Delay)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CLS&lt;/strong&gt; (Cumulative Layout Shift)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PageSpeed Insights&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebPageTest&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;in-summary-&quot;&gt;In Summary:&lt;/h3&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Web Performance&lt;/strong&gt; is the outcome of how well your &lt;strong&gt;DNS, SSL, CDN, compression, server, code, and browser&lt;/strong&gt; work together — to create a &lt;strong&gt;fast, smooth, and secure&lt;/strong&gt; experience.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It’s not just about tech — it's about making the &lt;strong&gt;web feel instant and reliable&lt;/strong&gt; for every user.&lt;/p&gt;
&lt;hr&gt;
</description>
<category>Web Dev</category>
<guid isPermaLink="true">https://coderlegion.com/3836/role-of-web-performance-in-the-web-architecture</guid>
<pubDate>Tue, 08 Jul 2025 20:23:12 +0000</pubDate>
</item>
<item>
<title>Find out the hot trick to make your project faster for users on load...</title>
<link>https://coderlegion.com/3122/find-out-the-hot-trick-to-make-your-project-faster-for-users-on-load</link>
<description>&lt;h2 id=&quot;why-lcp-and-inp-matter-for-every-developer&quot;&gt;Why LCP and INP Matter for Every Developer&lt;/h2&gt;
&lt;p&gt;As a developer, you might focus on writing clean code and adding cool features. But have you ever thought about how fast your website actually feels to users? That's where LCP and INP come in - two simple but important metrics that measure real user experience.  &lt;/p&gt;
&lt;h3 id=&quot;what-is-lcp-loading-speed-&quot;&gt;What is LCP? (Loading Speed)&lt;/h3&gt;
&lt;p&gt;LCP (Largest Contentful Paint) tracks how long it takes for the main content of your page to load. Imagine visiting a blog - LCP measures when the article title and first paragraph become visible.  &lt;/p&gt;
&lt;p&gt;A good LCP score is under 2.5 seconds. If it takes longer than 4 seconds, users will likely leave before seeing your content. The main culprits are usually large images, slow servers, or too much JavaScript blocking the page.  &lt;/p&gt;
&lt;h3 id=&quot;what-is-inp-responsiveness-&quot;&gt;What is INP? (Responsiveness)&lt;/h3&gt;
&lt;p&gt;INP (Interaction to Next Paint) measures how quickly your page responds when users click or tap something. It's the delay between clicking a button and seeing something happen.  &lt;/p&gt;
&lt;p&gt;For a smooth experience, INP should be under 200 milliseconds. Above 500ms, your site will feel sluggish and unresponsive. This often happens when there are too many JavaScript tasks running at once.  &lt;/p&gt;
&lt;h2 id=&quot;why-should-you-care-&quot;&gt;Why Should You Care?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Users leave slow sites&lt;/strong&gt; - Over half of visitors will abandon a page that takes more than 3 seconds to load  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google ranks faster sites higher&lt;/strong&gt; - These metrics directly affect your search rankings  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better user experience&lt;/strong&gt; - Fast sites keep people engaged and happy  &lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;easy-ways-to-improve&quot;&gt;Easy Ways to Improve&lt;/h2&gt;
&lt;p&gt;For LCP:  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compress and properly size images (use WebP format)  &lt;/li&gt;
&lt;li&gt;Load important content first  &lt;/li&gt;
&lt;li&gt;Reduce unnecessary JavaScript  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For INP:  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Avoid putting too many click handlers on elements  &lt;/li&gt;
&lt;li&gt;Show loading indicators immediately  &lt;/li&gt;
&lt;li&gt;Break up long JavaScript tasks  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;How to Check Your Scores&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;You don't need to be an expert to measure these:  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Chrome DevTools (F12)  &lt;/li&gt;
&lt;li&gt;Go to the Lighthouse tab  &lt;/li&gt;
&lt;li&gt;Run an audit - it will show your LCP and INP scores  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Start Small&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;You don't need to optimize everything at once. Try fixing just one issue this week - maybe compress your largest image or simplify a button click handler. Small improvements add up!  &lt;/p&gt;
&lt;h2 id=&quot;final-thought&quot;&gt;Final Thought&lt;/h2&gt;
&lt;p&gt;Building fast websites isn't just for experts. By paying attention to LCP and INP, you'll create better experiences for your users and stand out as a developer who cares about performance.  &lt;/p&gt;
&lt;p&gt;What's been your experience with website speed? Have you noticed any particularly fast or slow sites recently? Share your thoughts below!  &lt;/p&gt;
</description>
<category>Java</category>
<guid isPermaLink="true">https://coderlegion.com/3122/find-out-the-hot-trick-to-make-your-project-faster-for-users-on-load</guid>
<pubDate>Wed, 21 May 2025 16:31:28 +0000</pubDate>
</item>
<item>
<title>Offset vs Cursor-based Pagination: Choosing The Right Pagination Strategy</title>
<link>https://coderlegion.com/1758/offset-vs-cursor-based-pagination-choosing-the-right-pagination-strategy</link>
<description>&lt;p&gt;Whether you’re building API endpoints or designing sleek interfaces, selecting the right pagination strategy can significantly influence user experience and system performance. By the end of this article, you will understand the two types of pagination strategies and know when to choose one over the other.&lt;/p&gt;
&lt;h3 id=&quot;what-is-pagination-amp-why-does-it-matter-&quot;&gt;What is Pagination &amp;amp; Why Does It Matter?&lt;/h3&gt;
&lt;p&gt;Pagination is the process of dividing large datasets into smaller, manageable chunks or pages to improve performance and usability. Without pagination:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Frontend apps&lt;/em&gt; would struggle with rendering thousands of records at once.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Backend APIs&lt;/em&gt; would waste resources fetching unnecessary data.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;two-types-of-pagination-strategy&quot;&gt;Two Types Of Pagination Strategy&lt;/h3&gt;
&lt;p&gt;During the course of my career, I have seen engineers use the wrong pagination approach when implementing features for their app. Understanding the two types of pagination strategies can make a significant difference in both the performance and user experience of software applications.&lt;/p&gt;
&lt;h4 id=&quot;offset-based-pagination&quot;&gt;Offset-Based Pagination&lt;/h4&gt;
&lt;p&gt;Offset-based pagination is a commonly used pagination pattern where the frontend specifies a page number and a page size (how many items per page).&lt;/p&gt;
&lt;p&gt;Here is a sample React component that makes a request to an API that is based on offset pagination&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function ProductList() {
  const [products, setProducts] = useState([]);
  const [page, setPage] = useState(1);

  const fetchProducts = async () =&amp;gt; {
    const response = await fetch(`/api/products?page=${page}&amp;amp;limit=10`);
    const data = await response.json();
    setProducts(data);
  };

  return (
    &amp;lt;div&amp;gt;
      {products.map(product =&amp;gt; &amp;lt;div key={product.id}&amp;gt;{product.name}&amp;lt;/div&amp;gt;)}
      &amp;lt;button onClick={() =&amp;gt; setPage(p =&amp;gt; p - 1)} disabled={page === 1}&amp;gt;
        Previous
      &amp;lt;/button&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setPage(p =&amp;gt; p + 1)}&amp;gt;Next&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A sample sql query to implement the pagination could look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT * FROM products LIMIT 10 OFFSET 20;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;pros-of-offset-based-pagination&quot;&gt;Pros of offset-based pagination&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;It is easy to implement on the backend and also easy to implement a&lt;br&gt;
frontend component for interacting with the paginated data.&lt;/li&gt;
&lt;li&gt;Users can conveniently jump to any specific page.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;cons-of-offset-based-pagination&quot;&gt;Cons of offset-based pagination&lt;/h4&gt;
&lt;h4 id=&quot;performance-issues-with-large-datasets-&quot;&gt;Performance issues with large datasets:&lt;/h4&gt;
&lt;p&gt;While it is possible for the frontend to request a specific page number, the database still has to scan all the previous rows in the table until it reaches the exact page number and retrieve the specified page size. This leads to a significant performance bottle-neck for very large datasets. Let me make this clearer. Imagine a dataset of 500,000 records. To request the last 20 records, the frontend will make a GET request similar to this: &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;GET /api/items?page=25000&amp;amp;limit=20
   
// OR using the offset parameter
   
GET /api/items?offset=499980&amp;amp;limit=20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means, the database must scan all 500,000 rows (even though the frontend only wants 20), skip the first 499,980 records (wasting CPU, I/O, and memory) and finally return the last 20. This leads to &lt;em&gt;slow queries&lt;/em&gt; (higher latencies) and &lt;em&gt;wasted resources&lt;/em&gt; (imagine 1000 users hitting page 25,000 simultaneously!)&lt;/p&gt;
&lt;h4 id=&quot;inconsistent-results-with-real-time-data&quot;&gt;Inconsistent results with real-time data&lt;/h4&gt;
&lt;p&gt;Offset-based pagination is not suitable for real-time data such as social media feeds because it can lead to duplicate and inconsistent results. This is why social media apps such as Facebook, Instagram, Twitter, etc., use infinite scroll (which is based on cursor pagination) for loading more data to the UI.&lt;/p&gt;
&lt;h4 id=&quot;cursor-based-pagination&quot;&gt;Cursor-based pagination&lt;/h4&gt;
&lt;p&gt;Cursor-based pagination uses a unique identifier known as a &lt;em&gt;cursor&lt;/em&gt; or &lt;em&gt;pointer&lt;/em&gt; as a reference point to fetch the next set of results. That is, each time the frontend makes an API call, in addition to sending back data, the API would send a unique identifier that the frontend will pass in the next request. Then the API would use that identifier as a reference point for sending the next set of data.&lt;/p&gt;
&lt;p&gt;Below is a sample React component that consumes an API that is based on cursor pagination:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function CommentsFeed() {
  const [comments, setComments] = useState([]);
  const [cursor, setCursor] = useState(null);

  const fetchComments = async () =&amp;gt; {
    const url = `/api/comments?limit=10${cursor ? `&amp;amp;cursor=${cursor}` : ''}`;
    const response = await fetch(url);
    const { data, nextCursor } = await response.json();
    setComments(prev =&amp;gt; [...prev, ...data]);
    setCursor(nextCursor);
  };

  return (
    &amp;lt;div&amp;gt;
      {comments.map(comment =&amp;gt; &amp;lt;div key={comment.id}&amp;gt;{comment.content}&amp;lt;/div&amp;gt;)}
      {cursor &amp;amp;&amp;amp; &amp;lt;button onClick={fetchComments}&amp;gt;Load More&amp;lt;/button&amp;gt;}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;pros-of-cursor-based-pagination&quot;&gt;Pros of cursor-based pagination&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Efficient for large datasets since the database uses the unique&lt;br&gt;
identifier to retrieve the next set of data without having to scan&lt;br&gt;
previous records.&lt;/li&gt;
&lt;li&gt;Suitable for real-time data: Cursor based pagination does not lead to&lt;br&gt;
duplicate or inconsistent results when applied on datasets that&lt;br&gt;
update rapidly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;cons-of-cursor-based-pagination&quot;&gt;Cons of cursor-based pagination&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Users cannot jump to specific pages.&lt;/li&gt;
&lt;li&gt;It is slighltly more complex to implement.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;comparison-of-offset-vs-cursor&quot;&gt;Comparison of Offset vs. Cursor&lt;/h4&gt;
&lt;table&gt;
&lt;tr style=&quot;background-color: #000; color: #fff;&quot;&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Offset Pagination&lt;/th&gt;
&lt;th&gt;Cursor Pagination&lt;/th&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #000; color: #fff;&quot;&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Slow with large offsets&lt;/td&gt;
&lt;td&gt;Fast, even with big data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #000; color: #fff;&quot;&gt;
&lt;td&gt;&lt;strong&gt;Random Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No (sequential only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #000; color: #fff;&quot;&gt;
&lt;td&gt;&lt;strong&gt;Real-time Data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Pages may shift&lt;/td&gt;
&lt;td&gt;✅ Stable &amp;amp; consistent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #000; color: #fff;&quot;&gt;
&lt;td&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;Requires cursor tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #000; color: #fff;&quot;&gt;
&lt;td&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;E-commerce, dashboards&lt;/td&gt;
&lt;td&gt;Social media, activity logs&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Choosing the right pagination strategy depends on the context and requirements of your features. With the above insights, you're ready to paginate with precision—empowering your application to be both user-friendly and performant.&lt;/p&gt;
</description>
<category>Web Dev</category>
<guid isPermaLink="true">https://coderlegion.com/1758/offset-vs-cursor-based-pagination-choosing-the-right-pagination-strategy</guid>
<pubDate>Tue, 01 Apr 2025 08:52:38 +0000</pubDate>
</item>
<item>
<title>Non-functional requirements are the pillars that determine whether your application will thrive or fall apart</title>
<link>https://coderlegion.com/1223/functional-requirements-pillars-that-determine-whether-your-application-thrive-apart</link>
<description>&lt;p&gt;When we think about building applications, most of us start with the obvious—functional requirements. We focus on what the app &lt;em&gt;should do&lt;/em&gt;—the features, the user flows, and the outputs. But as your application grows, you quickly realize that the real challenges often lie in what the app &lt;em&gt;must be&lt;/em&gt;—secure, fast, always available, and easy to maintain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;That’s where non-functional requirements come in.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Inspired by Dr. Werner Vogels' insightful keynote at AWS re:Invent 2023, this series explores these often-overlooked aspects of application development. Non-functional requirements (NFRs) are not just technical jargon—they’re the pillars that determine whether your application will thrive or fall apart as it scales. Whether you’re building a startup MVP or managing a massive enterprise app, you’ll face these challenges.&lt;/p&gt;
&lt;h4 id=&quot;why-you-should-care-about-non-functional-requirements&quot;&gt;Why You Should Care About Non-Functional Requirements&lt;/h4&gt;
&lt;p&gt;Imagine launching an application that looks great and works well—until your first 10,000 users arrive, and it slows to a crawl. Or perhaps you’ve built an app that processes sensitive data but hasn’t accounted for the latest security protocols, leaving you vulnerable to a data breach. What about the cost of maintaining that app over time? How will you handle updates, compliance audits, and infrastructure scaling?&lt;/p&gt;
&lt;p&gt;This series isn’t about abstract theory—it’s about solving real problems. Each post will focus on a different non-functional requirement, breaking it down with &lt;strong&gt;real-world examples&lt;/strong&gt;, &lt;strong&gt;hands-on tutorials&lt;/strong&gt;, and insights into &lt;strong&gt;AWS services&lt;/strong&gt; that can make implementation smoother. Whether you're a developer, cloud architect, or DevOps engineer, this guide is for you.&lt;/p&gt;
&lt;h4 id=&quot;what-s-ahead-&quot;&gt;What’s Ahead?&lt;/h4&gt;
&lt;p&gt;We’ll cover the nine key non-functional requirements that every modern application needs to address:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compliance&lt;/strong&gt;  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accessibility&lt;/strong&gt;  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Availability&lt;/strong&gt;  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability&lt;/strong&gt;  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost&lt;/strong&gt;  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sustainability&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first three are &lt;strong&gt;non-negotiable&lt;/strong&gt;. They’re critical to protecting your users, data, and business. The remaining six require thoughtful trade-offs—you can’t have it all without some compromises, so we’ll dive into how to make those tough decisions.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;breaking-it-down-what-you-ll-learn&quot;&gt;Breaking It Down: What You’ll Learn&lt;/h4&gt;
&lt;p&gt;In this series, we’ll answer the &lt;strong&gt;5Ws (What, Why, When, Where, Who)&lt;/strong&gt; for each requirement, but we’ll also take things a step further. We’ll approach each topic from &lt;strong&gt;different angles&lt;/strong&gt; to ensure you get a well-rounded understanding of how these principles impact your applications, both technically and strategically.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hands-On Tutorials:&lt;/strong&gt; You'll get practical guides, walking you through the setup and optimization of key AWS services like &lt;strong&gt;AWS Shield, AWS Cognito, EC2 Auto Scaling&lt;/strong&gt;, and more.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real-World Case Studies:&lt;/strong&gt; We’ll learn from the successes and failures of companies that have tackled these challenges head-on, especially in high-stakes environments like &lt;strong&gt;e-commerce&lt;/strong&gt;, &lt;strong&gt;healthcare&lt;/strong&gt;, and &lt;strong&gt;startups&lt;/strong&gt;.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pitfalls &amp;amp; Lessons Learned:&lt;/strong&gt; Don’t fall into the common traps—discover what to avoid before it becomes a costly mistake.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expert Insights:&lt;/strong&gt; We’ll feature interviews with industry experts who live and breathe security, compliance, and cloud architecture.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost-Benefit Analysis:&lt;/strong&gt; Balancing trade-offs between performance, scalability, and cost efficiency will be a recurring theme.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;a-quick-look-at-what-s-coming&quot;&gt;A Quick Look at What’s Coming&lt;/h4&gt;
&lt;h5 id=&quot;security-the-bedrock-of-trust&quot;&gt;&lt;strong&gt;Security: The Bedrock of Trust&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;In today’s hyper-connected world, your app’s security is always under scrutiny. We’ll dive into encryption, authentication, and threat protection using &lt;strong&gt;AWS services like Cognito and AWS Shield&lt;/strong&gt;—plus a few non-AWS tools like &lt;strong&gt;Nessus&lt;/strong&gt; for vulnerability scanning. Expect step-by-step guidance, code examples, and real-world scenarios.&lt;/p&gt;
&lt;h5 id=&quot;compliance-navigating-the-regulatory-maze&quot;&gt;&lt;strong&gt;Compliance: Navigating the Regulatory Maze&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;Staying compliant with global regulations like &lt;strong&gt;GDPR&lt;/strong&gt;, &lt;strong&gt;HIPAA&lt;/strong&gt;, and &lt;strong&gt;SOC 2&lt;/strong&gt; is critical to keeping your business afloat. We’ll break down how AWS services like &lt;strong&gt;AWS Config&lt;/strong&gt; and &lt;strong&gt;AWS Artifact&lt;/strong&gt; help you meet these ever-evolving requirements, backed by a &lt;strong&gt;case study&lt;/strong&gt; on how a healthcare company successfully implemented compliance from day one.&lt;/p&gt;
&lt;h5 id=&quot;accessibility-designing-for-everyone&quot;&gt;&lt;strong&gt;Accessibility: Designing for Everyone&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;Accessibility is about more than ticking boxes—it’s about ensuring your app works for every user, regardless of their abilities. We’ll separate myth from fact, showing you how to use &lt;strong&gt;AWS Amplify&lt;/strong&gt;, &lt;strong&gt;React’s accessibility features&lt;/strong&gt;, and tools like &lt;strong&gt;axe DevTools&lt;/strong&gt; to make your app more inclusive. &lt;/p&gt;
&lt;h5 id=&quot;performance-faster-smoother-better&quot;&gt;&lt;strong&gt;Performance: Faster, Smoother, Better&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;Performance optimization isn’t just about reducing latency; it’s about providing a seamless user experience, even under load. We’ll explore &lt;strong&gt;AWS CloudFront&lt;/strong&gt; for fast content delivery, and look at how &lt;strong&gt;serverless&lt;/strong&gt; can be a game-changer for scaling apps efficiently.&lt;/p&gt;
&lt;h5 id=&quot;availability-your-app-always-on&quot;&gt;&lt;strong&gt;Availability: Your App, Always On&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;How do you keep your app running, even during server failures or spikes in traffic? We’ll tackle &lt;strong&gt;high availability&lt;/strong&gt; with multi-region architectures and services like &lt;strong&gt;Elastic Load Balancing (ELB)&lt;/strong&gt; and &lt;strong&gt;Route 53&lt;/strong&gt;, ensuring your app has zero downtime, no matter what.&lt;/p&gt;
&lt;h5 id=&quot;scalability-ready-to-grow-&quot;&gt;&lt;strong&gt;Scalability: Ready to Grow?&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;As your user base grows, your app needs to grow with it. We’ll break down horizontal vs. vertical scaling, using &lt;strong&gt;AWS EC2 Auto Scaling&lt;/strong&gt;, &lt;strong&gt;EKS&lt;/strong&gt;, and more. Plus, we’ll discuss how to manage costs while scaling efficiently—a key consideration for any growing business.&lt;/p&gt;
&lt;h5 id=&quot;maintainability-preparing-for-the-long-haul&quot;&gt;&lt;strong&gt;Maintainability: Preparing for the Long Haul&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;Building maintainable systems saves you time and headaches in the long run. We’ll share best practices for code organization, testing, and CI/CD using AWS services like &lt;strong&gt;AWS CodeBuild&lt;/strong&gt;, &lt;strong&gt;CodeDeploy&lt;/strong&gt;, and &lt;strong&gt;CloudWatch&lt;/strong&gt;. You’ll learn how to keep your app running smoothly, even as it evolves.&lt;/p&gt;
&lt;h5 id=&quot;cost-optimizing-for-efficiency&quot;&gt;&lt;strong&gt;Cost: Optimizing for Efficiency&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;Cost isn’t just about cutting expenses; it’s about balancing performance, availability, and scalability in the most efficient way. We’ll explore AWS tools like &lt;strong&gt;Cost Explorer&lt;/strong&gt;, &lt;strong&gt;AWS Budgets&lt;/strong&gt;, and &lt;strong&gt;Trusted Advisor&lt;/strong&gt;, along with strategies for minimizing costs without compromising quality.&lt;/p&gt;
&lt;h5 id=&quot;sustainability-building-green-applications&quot;&gt;&lt;strong&gt;Sustainability: Building Green Applications&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;Cloud sustainability is becoming increasingly important. In this post, we’ll explore how to use &lt;strong&gt;AWS Lambda&lt;/strong&gt; and &lt;strong&gt;DynamoDB&lt;/strong&gt; to build energy-efficient, cost-effective applications while reducing your app’s carbon footprint. Plus, we’ll feature insights from experts who are leading the charge in sustainable architecture.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;why-this-series-matters&quot;&gt;Why This Series Matters&lt;/h4&gt;
&lt;p&gt;In an era where applications must be fast, scalable, and secure—all while managing costs and sustainability—the importance of non-functional requirements cannot be overstated. These are the elements that ensure your application doesn’t just survive, but thrives in a competitive, rapidly evolving tech landscape.&lt;/p&gt;
&lt;p&gt;Whether you're building your first MVP or managing a complex cloud infrastructure for millions of users, this series will equip you with the tools, strategies, and insights you need to build more resilient, reliable, and efficient applications.&lt;/p&gt;
&lt;p&gt;Let’s dive in together. &lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Cover Image:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/UTRBVPvzt9w?t=994&quot;&gt;AWS re:Invent 2023 - Keynote with Dr. Werner Vogels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</description>
<category>Cloud Computing</category>
<guid isPermaLink="true">https://coderlegion.com/1223/functional-requirements-pillars-that-determine-whether-your-application-thrive-apart</guid>
<pubDate>Mon, 24 Feb 2025 21:48:29 +0000</pubDate>
</item>
<item>
<title>NeoPopup - The Modern 3D Popup Module</title>
<link>https://coderlegion.com/591/neopopup-the-modern-3d-popup-module</link>
<description>&lt;h2 id=&quot;neopopup---the-modern-3d-popup-module&quot;&gt;NeoPopup - The Modern 3D Popup Module&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MayankChawdhari/NeoPopup&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/version-1.0.0-blue&quot; alt=&quot;Version&quot;&gt;&lt;/a&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;LICENSE&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/license-MIT-brightgreen&quot; alt=&quot;License&quot;&gt;&lt;/a&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MayankChawdhari&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/author-Mayank%20Chawdhari-orange&quot; alt=&quot;Developer&quot;&gt;&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;NeoPopup is a modern, customizable, and responsive 3D popup module designed for sleek and professional web interfaces. With options for themes, animations, and customization, this module makes adding a stunning popup to your website effortless.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;features&quot;&gt;Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3D Popup Design&lt;/strong&gt;: Eye-catching 3D animations for modern UI.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customizable&lt;/strong&gt;: Easily adjust text, colors, size, theme, and more.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auto-Close&lt;/strong&gt;: Optional auto-close functionality with customizable duration.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Persistent Mode&lt;/strong&gt;: Remember user preferences using local storage.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Responsive&lt;/strong&gt;: Works seamlessly across devices and screen sizes.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Light/Dark Themes&lt;/strong&gt;: Built-in support for theme selection.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Positioning&lt;/strong&gt;: Display popups at any corner of the screen.  &lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;preview&quot;&gt;Preview&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/368cba4e-e624-48d4-8c29-4772baa06751&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;installation&quot;&gt;Installation&lt;/h3&gt;
&lt;p&gt;Clone the repository or download the module files:  &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/BOSS294/NeoPopup.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Include the JavaScript file in your project:  &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script src=&quot;path/to/developmentPopup.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;usage&quot;&gt;Usage&lt;/h3&gt;
&lt;h4 id=&quot;basic-example&quot;&gt;Basic Example&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;developmentPopup.init({
    title: &quot;Welcome to NeoPopup!&quot;,
    body: &quot;Thank you for exploring the modern 3D popup module.&quot;,
    buttonText: &quot;Learn More&quot;,
    buttonCallback: () =&amp;gt; window.open('https://github.com/BOSS294/NeoPopup', '_blank'),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;available-options&quot;&gt;Available Options&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Option          &lt;/th&gt;&lt;th&gt;Type          &lt;/th&gt;&lt;th&gt;Default              &lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;title&lt;/code&gt;         &lt;/td&gt;&lt;td&gt;&lt;code&gt;string&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;Under Development&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Title of the popup.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;body&lt;/code&gt;          &lt;/td&gt;&lt;td&gt;&lt;code&gt;string&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;&amp;lt;strong&amp;gt;Oops!&amp;lt;/strong&amp;gt;&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Body content (HTML supported).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;buttonText&lt;/code&gt;    &lt;/td&gt;&lt;td&gt;&lt;code&gt;string&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;Check Latest Updates&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Text for the main button.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;buttonCallback&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;function&lt;/code&gt;    &lt;/td&gt;&lt;td&gt;&lt;code&gt;null&lt;/code&gt;               &lt;/td&gt;&lt;td&gt;Callback function for the main button click.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;position&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;string&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;bottom-right&quot;&lt;/code&gt;     &lt;/td&gt;&lt;td&gt;Popup position: &lt;code&gt;top-right&lt;/code&gt;, &lt;code&gt;top-left&lt;/code&gt;, &lt;code&gt;bottom-right&lt;/code&gt;, or &lt;code&gt;bottom-left&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;theme&lt;/code&gt;         &lt;/td&gt;&lt;td&gt;&lt;code&gt;string&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;dark&quot;&lt;/code&gt;            &lt;/td&gt;&lt;td&gt;Theme for the popup: &lt;code&gt;dark&lt;/code&gt; or &lt;code&gt;light&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;colors&lt;/code&gt;        &lt;/td&gt;&lt;td&gt;&lt;code&gt;object&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;{}&lt;/code&gt;                 &lt;/td&gt;&lt;td&gt;Custom colors: &lt;code&gt;{ background, text, button }&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;size&lt;/code&gt;          &lt;/td&gt;&lt;td&gt;&lt;code&gt;object&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;{}&lt;/code&gt;                 &lt;/td&gt;&lt;td&gt;Size options: &lt;code&gt;{ width, height }&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;animation&lt;/code&gt;     &lt;/td&gt;&lt;td&gt;&lt;code&gt;string&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;slide&quot;&lt;/code&gt;           &lt;/td&gt;&lt;td&gt;Animation type.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;autoClose&lt;/code&gt;     &lt;/td&gt;&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;     &lt;/td&gt;&lt;td&gt;&lt;code&gt;false&lt;/code&gt;              &lt;/td&gt;&lt;td&gt;Automatically close the popup.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;closeDuration&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;number&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;5000&lt;/code&gt;               &lt;/td&gt;&lt;td&gt;Auto-close duration in milliseconds.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;persistent&lt;/code&gt;    &lt;/td&gt;&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;     &lt;/td&gt;&lt;td&gt;&lt;code&gt;false&lt;/code&gt;              &lt;/td&gt;&lt;td&gt;Prevent popup from showing repeatedly using local storage.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;styling&quot;&gt;Styling&lt;/h3&gt;
&lt;p&gt;To include NeoPopup's built-in styling, ensure the following:  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;DP-popup-wrapper&lt;/code&gt; div is included in your HTML.&lt;/li&gt;
&lt;li&gt;NeoPopup comes with &lt;strong&gt;3D animations&lt;/strong&gt;, &lt;strong&gt;shadow effects&lt;/strong&gt;, and customizable themes.  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To customize the design further, modify the included CSS:  &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.DP-popup-content {
    background: linear-gradient(135deg, #4a90e2, #2a3b4c);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;example-with-persistent-mode&quot;&gt;Example with Persistent Mode&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;developmentPopup.init({
    title: &quot;We Value Your Feedback&quot;,
    body: &quot;Click below to share your thoughts!&quot;,
    buttonText: &quot;Give Feedback&quot;,
    buttonCallback: () =&amp;gt; alert('Thank you!'),
    persistent: true,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;contributing&quot;&gt;Contributing&lt;/h3&gt;
&lt;p&gt;Contributions are always welcome! Here's how you can help:  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fork the repository.  &lt;/li&gt;
&lt;li&gt;Create your feature branch: &lt;code&gt;git checkout -b feature/AmazingFeature&lt;/code&gt;.  &lt;/li&gt;
&lt;li&gt;Commit your changes: &lt;code&gt;git commit -m &quot;Add some AmazingFeature&quot;&lt;/code&gt;.  &lt;/li&gt;
&lt;li&gt;Push to the branch: &lt;code&gt;git push origin feature/AmazingFeature&lt;/code&gt;.  &lt;/li&gt;
&lt;li&gt;Open a pull request.  &lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;support&quot;&gt;Support&lt;/h3&gt;
&lt;p&gt;For any queries or feature requests:  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Email&lt;/strong&gt;: [*Emails are not allowed*](mailto:&lt;em&gt;Emails are not allowed&lt;/em&gt;)  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Issues&lt;/strong&gt;: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/BOSS294/NeoPopup/issues&quot;&gt;NeoPopup Issues&lt;/a&gt;  &lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;license&quot;&gt;License&lt;/h3&gt;
&lt;p&gt;This project is licensed under the MIT License. See the &lt;a rel=&quot;nofollow&quot; href=&quot;LICENSE&quot;&gt;LICENSE&lt;/a&gt; file for details.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Developed with ❤️ by &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/BOSS294&quot;&gt;Mayank Chawdhari&lt;/a&gt;&lt;/strong&gt;.   &lt;/p&gt;
</description>
<category>JavaScript</category>
<guid isPermaLink="true">https://coderlegion.com/591/neopopup-the-modern-3d-popup-module</guid>
<pubDate>Tue, 07 Jan 2025 06:32:52 +0000</pubDate>
</item>
<item>
<title>Rate limiting middleware in ASP.NET Core using .NET 8.0</title>
<link>https://coderlegion.com/277/rate-limiting-middleware-in-asp-net-core-using-net-8-0</link>
<description>&lt;p&gt;&lt;strong&gt;Rate limiting middleware&lt;/strong&gt; in ASP.NET Core is a concept commonly associated with an API, where it states (&lt;strong&gt;a rate limiter in a web API&lt;/strong&gt;) is a mechanism used to &lt;strong&gt;control&lt;/strong&gt; the number of requests that a client can make to an API within a certain time frame. It helps prevent API misuse or abuse by limiting the number of requests from a particular client or IP address, ensuring &lt;strong&gt;fair usage&lt;/strong&gt;, and &lt;strong&gt;protecting server resources&lt;/strong&gt; from being overwhelmed.&lt;/p&gt;
&lt;div class=&quot;div-blue&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;This concept has been integrated as a middleware with .NET 7 and later.&lt;/span&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;p&gt;In the .Net environment, we have 4 types of algorithms that we can use in this regard:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1) Fixed window limiter&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2) Sliding window limiter&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3) Token bucket limiter&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4) Concurrency limiter&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We will explain each of them in a simplified manner.&lt;/p&gt;
&lt;h2 id=&quot;1-fixed-window-limiter&quot;&gt;1) Fixed window limiter&lt;/h2&gt;
&lt;p&gt;Fixed window limiter stipulates that there is a time limit for the window in which a number of requests can be executed, meaning that the number of requests must not exceed the permitted number in a specific, pre-determined time.&lt;/p&gt;
&lt;p&gt;For example, 6 seconds for the window. You can execute a number of requests, for example 1 requests. If it exceeds this number, it must go to the waiting queue, for which we can also prepare an expected space, for example, 1 waiting requests. If the number of requests exceeds this number, an error message will be returned to the user because he has exceeded his permissible limit of requests, in most cases 429 in the case of an answer.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;builder.Services.AddRateLimiter(option =&amp;gt;
{
    option.AddFixedWindowLimiter(&quot;FixedWindowPolicy&quot;, opt =&amp;gt;
    {
        opt.PermitLimit = 1; // items allow processing
        opt.Window = TimeSpan.FromSeconds(6); // time need to process PermitLimit items's
        opt.QueueLimit = 1; // items allow be in Queue waiting

        opt.QueueProcessingOrder = System.Threading.RateLimiting.QueueProcessingOrder.OldestFirst;//Order the items in Queue Waiting
    }).RejectionStatusCode = 429; // This means that this request is not allowed at this time due to the large number of requests received now
});
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-sliding-window-limiter&quot;&gt;2) Sliding Window Limiter&lt;/h2&gt;
&lt;p&gt;The sliding window limiter is similar to the fixed window limiter but offers more flexibility. It processes old requests while tracking ongoing ones, allowing it to handle new requests based on the status of previously submitted but incomplete requests.&lt;/p&gt;
&lt;p&gt;For example, I have a window that lasts 6 seconds and receives 4 requests for this window. I can divide the window into three sliding windows (three segments per window). Here, this one window processes 4 requests. It executes what it can from it and passes it to the second window. And so, it goes for the second window as well. It executes and passes it to the window. The first is that if the sliding window has space to receive new requests, it pops up again in order to receive new requests while taking into account old requests that have not been completed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;builder.Services.AddRateLimiter(option =&amp;gt;
{
    option.AddSlidingWindowLimiter(&quot;SlidWindowPolicy&quot;, opt =&amp;gt;
    {
        opt.PermitLimit = 4; // items allow processing
        opt.Window = TimeSpan.FromSeconds(6); // time need to process PermitLimit items's
        opt.QueueLimit = 5; // items allow be in Queue waiting
        opt.QueueProcessingOrder = System.Threading.RateLimiting.QueueProcessingOrder.OldestFirst;//Order the items in Queue Waiting
        opt.SegmentsPerWindow = 3;
    }).RejectionStatusCode = 429; // This means that this request is not allowed at this time due to the large number of requests received now
});
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-token-bucket-limiter&quot;&gt;3) Token Bucket Limiter&lt;/h2&gt;
&lt;p&gt;The token bucket limiter is a straightforward yet effective rate limiting technique. It uses a bucket to hold a set number of tokens, each representing permission to handle one request. When a request is made, a token is removed from the bucket. If the bucket is empty, the request is denied until more tokens become available.Tokens are replenished at a fixed rate over time, ensuring that the system can handle bursts of requests followed by idle periods.  &lt;/p&gt;
&lt;p&gt;For example, in a token bucket rate limiting algorithm, you might have a bucket that can hold up to 100 tokens. Each incoming request consumes one token, so the bucket's token count decreases with each request. To ensure that the system can handle bursts of requests, new tokens are added to the bucket at a specified interval (e.g., 3 tokens every 5 seconds), without exceeding the maximum bucket size of 100 tokens. This mechanism prevents request flooding while allowing occasional spikes in request rate.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;builder.Services.AddRateLimiter(option =&amp;gt;
{
    option.AddTokenBucketLimiter(&quot;TokenBucketPolicy&quot;, opt =&amp;gt;
    {
        opt.TokenLimit = 100; // Maximum number of tokens in the bucket
        opt.QueueLimit = 10; // Number of requests that can wait in the queue
        opt.QueueProcessingOrder = System.Threading.RateLimiting.QueueProcessingOrder.OldestFirst; // Order of processing queued requests
        opt.ReplenishmentPeriod = TimeSpan.FromSeconds(5); // Time period for adding new tokens
        opt.TokensPerPeriod = 3; // Number of tokens added each period
    }).RejectionStatusCode = 429; // Status code returned when rate limit is exceeded
});
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4-concurrency-limiter&quot;&gt;4) Concurrency limiter&lt;/h2&gt;
&lt;p&gt;Concurrency limiter limits the number of concurrent requests. Each request reduces the concurrency limit by one. When the order is completed, the limit is increased by one.&lt;/p&gt;
&lt;p&gt;For example, we have a synchronization limit of 1 operation. Here, only one will be responsible for performing 1 simultaneous operation, meaning it must not exceed this limit.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;builder.Services.AddRateLimiter(option =&amp;gt;
{
    option.AddConcurrencyLimiter(&quot;ConcurrencyPolicy&quot;, opt =&amp;gt;
    {
        opt.PermitLimit = 1; // Maximum number of concurrent requests allowed
        opt.QueueLimit = 10; // Number of requests that can wait in the queue
        opt.QueueProcessingOrder = System.Threading.RateLimiting.QueueProcessingOrder.OldestFirst; // Order of processing queued requests
    }).RejectionStatusCode = 429; // Status code returned when rate limit is exceeded
});
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;In the end, to connect it to the endpoint you can use two methods:&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;by-using-controller-endpoint-&quot;&gt;&lt;strong&gt;By using Controller endpoint:&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;[HttpGet(&quot;GetConcurrency&quot;)]
[EnableRateLimiting(&quot;ConcurrencyWindowPolicy&quot;)]

public IActionResult GetConcurrency()
{
    return Ok(&quot;Concurrency&quot;);
}  

[HttpGet(&quot;GetFixed&quot;)]
[EnableRateLimiting(&quot;FixedWindowPolicy&quot;)]

public IActionResult GetFixed()
{
    return Ok(&quot;Fixed&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[DisableRateLimiting]
public ActionResult NoLimit()
{
    return Ok(&quot;NoLimit&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;EnableRateLimiting&lt;/strong&gt; attribute is used to apply a specified rate limiting policy to an action method. When this attribute is applied to a method, the rate limiting rules defined by the specified policy are enforced for that method. This means that any incoming requests to the action will be subject to the constraints of the policy, such as the maximum number of requests allowed in a given time period.&lt;/p&gt;
&lt;h3 id=&quot;by-using-minimal-endpoint-&quot;&gt;&lt;strong&gt;By using Minimal endpoint:&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt; app.MapGet(&quot;/&quot;, () =&amp;gt; Results.Ok(&quot;fixed&quot;)).RequireRateLimiting(&quot;FixedWindowPolicy&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;RequireRateLimiting&lt;/strong&gt; method is similar to &lt;em&gt;EnableRateLimiting&lt;/em&gt;, but it is used in the context of endpoint routing. It attaches a rate limiting policy directly to an endpoint defined in the routing configuration. When a request matches the route, the specified rate limiting policy is applied.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;importance-of-rate-limiting-for-api-providers&quot;&gt;Importance of Rate Limiting for API Providers&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. Maintaining Service Reliability&lt;/strong&gt;&lt;br&gt;
Rate limiting helps ensure that an API remains responsive and available, even under heavy load. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Preventing Abuse&lt;/strong&gt;&lt;br&gt;
APIs are often targets for malicious activities, such as denial-of-service (DoS) attacks, scraping, and other forms of abuse. Rate limiting acts as a safeguard against these threats by limiting the rate at which requests can be made, thereby reducing the impact of automated attacks and ensuring that legitimate users can access the service.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Ensuring Fair Resource Allocation&lt;/strong&gt;&lt;br&gt;
Rate limiting ensures that resources are distributed fairly among all users. Without rate limiting, a few users might consume a disproportionate amount of server resources, leading to poor performance or service degradation for others. &lt;/p&gt;
&lt;h2 id=&quot;in-the-end&quot;&gt;&lt;strong&gt;In the end&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;In the end, these rate limiting algorithms can be fine-tuned using additional parameters, such as client IP addresses or user authorization levels, to impose more detailed limits tailored to specific user profiles. By implementing rate limiting with precise parameters, API providers can ensure optimal performance, fair resource allocation, and protection against abuse across all user interactions with the API.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fine-Tuning Rate Limiting&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Client IP Addresses:&lt;/strong&gt; Apply rate limits based on the IP address of the client, allowing more granular control over request rates from different sources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;User Authorization Levels:&lt;/strong&gt; Differentiate rate limits based on user roles or authorization levels, providing higher limits for premium users and lower limits for regular users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Custom Rules:&lt;/strong&gt; Create custom rate limiting rules to accommodate specific application needs, such as limiting requests based on geographic regions or specific API endpoints.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;More resources &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/watch?v=bOfOo3Zsfx0&quot;&gt;https://www.youtube.com/watch?v=bOfOo3Zsfx0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?view=aspnetcore-8.0&quot;&gt;https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?view=aspnetcore-8.0&lt;/a&gt;&lt;/p&gt;
</description>
<category>ASP.NET</category>
<guid isPermaLink="true">https://coderlegion.com/277/rate-limiting-middleware-in-asp-net-core-using-net-8-0</guid>
<pubDate>Sat, 11 May 2024 20:54:38 +0000</pubDate>
</item>
<item>
<title>Python on the web - High cost of synchronous uWSGI</title>
<link>https://coderlegion.com/254/python-on-the-web-high-cost-of-synchronous-uwsgi</link>
<description>&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Python&lt;/strong&gt; is a popular choice for building programming due to its simplicity, readability, and vast ecosystem. Extending python as a webserver in a production environment is a little tricky due to the python &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Global_interpreter_lock&quot;&gt;&lt;strong&gt;GIL&lt;/strong&gt;&lt;/a&gt;. It basically renders most CPU bound multithreading pointless. We look into a popular option used widely and when and how it fails.&lt;/p&gt;
&lt;h3 id=&quot;python-on-web&quot;&gt;Python on Web&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Python interpreter&lt;/strong&gt; is basically an instance of our python application. We need a bridge between Python interpreter and the webserver to allow it to serve web requests. There are couple of popular standards for this bridge. We have WSGI and the latest ASGI standard. ASGI is a newer standard more suited for the latest async capabilities of python. Whereas WSGI is more suite for the traditional synchronous python.&lt;/p&gt;
&lt;p&gt;In this article, we are going to focus on how CPU bound tasks can lead to terrible user experience with WSGI.&lt;/p&gt;
&lt;div class=&quot;div-blue&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;&lt;strong&gt;WSGI Standard&lt;/strong&gt; helps us in extending python support to the web, which makes creating web applications using python possible.
One popular implementation in this standard is the &lt;strong&gt;uWSGI&lt;/strong&gt;.&lt;/span&gt;
&lt;/div&gt;
&lt;h3 id=&quot;how-uwsgi-works&quot;&gt;How uWSGI works&lt;/h3&gt;
&lt;p&gt;When we start a uWSGI instance for our Python application, it creates a worker process that runs an instance of the Python application with our application code imported. And if we have x number of worker process defined, it will fork until we have &lt;strong&gt;x&lt;/strong&gt; number of interpreters running.&lt;/p&gt;
&lt;div class=&quot;div-green&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Tip:&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;We talk about &lt;strong&gt;uWSGI workers&lt;/strong&gt; as processes, but &lt;strong&gt;uWSGI workers&lt;/strong&gt; can be &lt;strong&gt;thread&lt;/strong&gt; based as well. However they hardly ever work without issues for any python web application. The main reason behind this is the ecosystem and libraries, with many being &lt;strong&gt;NOT threadsafe&lt;/strong&gt;.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;NOT threadsafe&lt;/strong&gt; handles incoming HTTP requests and forward them to one of the available worker processes. Each worker process has its own &lt;strong&gt;GIL&lt;/strong&gt;, runs in its &lt;strong&gt;own memory space&lt;/strong&gt;, ensuring memory isolation and preventing issues like race conditions and data corruption that can occur when sharing state between threads.&lt;/p&gt;
&lt;h3 id=&quot;experiment-with-uwsgi&quot;&gt;Experiment with uWSGI&lt;/h3&gt;
&lt;p&gt;With our concepts clear, let's do a little experiment. We will see how incoming requests won't be served when the &lt;strong&gt;NOT threadsafe&lt;/strong&gt; workers are not available.&lt;/p&gt;
&lt;h4 id=&quot;setup&quot;&gt;Setup&lt;/h4&gt;
&lt;p&gt;We will create a &lt;strong&gt;NOT threadsafe&lt;/strong&gt; which will handle incoming requests. It is served by &lt;strong&gt;NOT threadsafe&lt;/strong&gt; with &lt;strong&gt;5 worker processes&lt;/strong&gt;. It will take &lt;strong&gt;10s&lt;/strong&gt; to respond to each request.&lt;/p&gt;
&lt;p&gt;We will create a client app while will make several (20) calls in parallel to the server at once. &lt;/p&gt;
&lt;div class=&quot;div-green&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Tip:&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;You can directly get the code from the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/viv1/blog-code-examples/tree/main/uWSGI_blog&quot;&gt;repository&lt;/a&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Write a simple python application that return &quot;true&quot;. Call it &lt;strong&gt;app_main.py&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;
import time

def long_running_task():
    time.sleep(10)
    return &quot;true&quot;

def application(env, start_response):
    query_string = env.get('QUERY_STRING', '')

    query_params = {}
    for param in query_string.split('&amp;amp;'):
        key, value = param.split('=')
        query_params[key] = value

    # Get the 'param' value from the query parameters
    param_value = query_params.get('param', 'No param value provided')

    print(f&quot;Accept incoming request: {param_value}&quot;)
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return [long_running_task().encode()]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;Dockerfile&lt;/code&gt; to do platform &amp;amp; OS independent setup for &lt;strong&gt;uWSGI&lt;/strong&gt;&lt;br&gt;
 (Assuming you have docker installed, otherwise you can install via Docker website: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.docker.com/get-started/&quot;&gt;https://www.docker.com/get-started/&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;
    # Use an official Python runtime as a parent image
    FROM python:3.9
    
    # Set the working directory in the container
    WORKDIR /app
    
    # Install uWSGI and vim
    RUN apt-get update &amp;amp;&amp;amp; apt-get install -y build-essential vim
    RUN pip install uwsgi
    
    # Copy the app.py file into the container at /app
    COPY app_main.py /app
    
    # Make port 8000 available to the world outside this container
    EXPOSE 8000
    
    # Define environment variable
    ENV NAME World
    
    # Run uWSGI
    CMD [&quot;uwsgi&quot;, &quot;--http&quot;, &quot;0.0.0.0:8000&quot;, &quot;--wsgi-file&quot;, &quot;app_main.py&quot;, &quot;--master&quot;, &quot;--processes&quot;, &quot;5&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Create a client that will make calls in parallel to the web server. Call it &lt;strong&gt;client.py&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;
import requests
from concurrent.futures import ThreadPoolExecutor

def send_request(query_param):
    print(f&quot;Accept request: {query_param}&quot;)
    url = f'http://localhost:8000?param={query_param}'
    response = requests.get(url)
    print(f&quot;Response: {response.text}&quot;)

def send_requests():
    with ThreadPoolExecutor(max_workers=20) as executor:
        futures = [executor.submit(send_request, f'Request_{i}') for i in range(20)]

if __name__ == &quot;__main__&quot;:
    send_requests()
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;run-the-tests&quot;&gt;Run the tests&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;In one terminal, run the following to start our &lt;strong&gt;uWSGI&lt;/strong&gt; server:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;
    docker build -t blog-uwsgi -f Dockerfile .
    docker run -p 8000:8000 blog-uwsgi
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;In another terminal, run the following to setup client app:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;
python3 -m venv venv
source venv/bin/activate 
pip install uwsgi
pip install requests
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Now, run the client app:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;
python client.py
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;results&quot;&gt;Results&lt;/h4&gt;
&lt;p&gt;This is my output:&lt;br&gt;
&lt;/p&gt;&lt;pre&gt;&lt;code&gt;
&lt;code&gt;*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 1)
spawned uWSGI worker 1 (pid: 6, cores: 1)
spawned uWSGI worker 2 (pid: 7, cores: 1)
spawned uWSGI worker 3 (pid: 8, cores: 1)
spawned uWSGI worker 4 (pid: 9, cores: 1)
spawned uWSGI worker 5 (pid: 10, cores: 1)
spawned uWSGI http 1 (pid: 11)
Accept incoming request: Request_6
Accept incoming request: Request_4
Accept incoming request: Request_0
Accept incoming request: Request_1
Accept incoming request: Request_7
[pid: 8|app: 0|req: 1/1] 192.168.65.1 () {32 vars in 405 bytes} [Thu Apr 11 05:04:21 2024] GET /?param=Request_0 =&amp;gt; generated 4 bytes in 10005 msecs (HTTP/1.1 200) 1 headers in 45 bytes (1 switches on core 0)
Accept incoming request: Request_9
[pid: 6|app: 0|req: 1/2] 192.168.65.1 () {32 vars in 405 bytes} [Thu Apr 11 05:04:21 2024] GET /?param=Request_4 =&amp;gt; generated 4 bytes in 10006 msecs (HTTP/1.1 200) 1 headers in 45 bytes (1 switches on core 0)
Accept incoming request: Request_10
[pid: 7|app: 0|req: 1/3] 192.168.65.1 () {32 vars in 405 bytes} [Thu Apr 11 05:04:21 2024] GET /?param=Request_7 =&amp;gt; generated 4 bytes in 10005 msecs (HTTP/1.1 200) 1 headers in 45 bytes (1 switches on core 0)
Accept incoming request: Request_8
[pid: 10|app: 0|req: 1/4] 192.168.65.1 () {32 vars in 405 bytes} [Thu Apr 11 05:04:21 2024] GET /?param=Request_6 =&amp;gt; generated 4 bytes in 10008 msecs (HTTP/1.1 200) 1 headers in 45 bytes (1 switches on core 0)
Accept incoming request: Request_2
[pid: 9|app: 0|req: 1/5] 192.168.65.1 () {32 vars in 405 bytes} [Thu Apr 11 05:04:21 2024] GET /?param=Request_1 =&amp;gt; generated 4 bytes in 10016 msecs (HTTP/1.1 200) 1 headers in 45 bytes (1 switches on core 0)
Accept incoming request: Request_11
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;(Observe the &lt;strong&gt;Accept incoming request:&lt;/strong&gt; lines in the above &lt;strong&gt;uWSGI&lt;/strong&gt; server output).&lt;/p&gt;
&lt;p&gt;Notice how even when 20 requests were initiated by the client, the &lt;strong&gt;uWSGI&lt;/strong&gt; server could only handle 5 requests at once. All the 5 &lt;code&gt;worker processes&lt;/code&gt; were tied up. Until those requests were not served, no incoming request was being handled. As soon as a worker process became free, it took up another incoming request.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Python's &quot;GIL&quot; limitation is mitigated to some extent by &quot;uWSGI's&quot; ability to spawn multiple worker processes, however it is not enough when dealing with computationally intensive operations.&lt;/p&gt;
&lt;p&gt;One solution, that is used frequently, is offloading CPU-bound tasks to separate processes or services that run independently in background (example &quot;celery&quot;). This allows the uWSGI processes to handle incoming requests more efficiently.&lt;/p&gt;
&lt;p&gt;The evolution of web frameworks and standards continues to address the limitations observed in traditional &quot;WSGI&quot;-based applications, with &quot;ASGI&quot; being the latest trend.&lt;/p&gt;
&lt;p&gt;&quot;ASGI&quot; offers a solution to the synchronous limitations of &quot;WSGI&quot;, enabling Python applications to handle a large number of concurrent connections efficiently. This is particularly beneficial for I/O-bound and high-concurrency applications and specially when combined with python &quot;asyncio&quot;, where the traditional synchronous processing model of WSGI shows its limitations.&lt;/p&gt;
&lt;h3 id=&quot;references-and-further-reading&quot;&gt;References and Further Reading&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Web_Server_Gateway_Interface&quot;&gt;WSGI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html&quot;&gt;uWSGI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://asgi.readthedocs.io/&quot;&gt;ASGI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://realpython.com/async-io-python/&quot;&gt;asyncio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://realpython.com/asynchronous-tasks-with-django-and-celery/&quot;&gt;celery&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;div-blue&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;This article was originally published in my &lt;a rel=&quot;nofollow&quot; href=&quot;https://wewake.dev/posts/high-cost-of-sync-uwsgi/&quot;&gt;personal blog&lt;/a&gt;.&lt;/span&gt;
&lt;/div&gt;
</description>
<category>Python</category>
<guid isPermaLink="true">https://coderlegion.com/254/python-on-the-web-high-cost-of-synchronous-uwsgi</guid>
<pubDate>Tue, 30 Apr 2024 22:31:09 +0000</pubDate>
</item>
</channel>
</rss>