Troubleshooting a slow website

Last updated Jun 16, 2026 This guide helps you identify and resolve performance issues affecting your website. It starts with basic diagnostics and progresses to advanced troubleshooting techniques.

Before you start: Verify traffic goes through Cloudflare

Before troubleshooting performance, confirm that your traffic is actually going through Cloudflare. If requests bypass Cloudflare, the issue is not related to Cloudflare and this guide will not help.

Check for the cf-ray header

Every response served through Cloudflare includes a cf-ray header. Check for this header:

curl -s -D- -o /dev/null https://www.example.com | grep -i cf-ray
(Invoke-WebRequest -Uri "https://www.example.com" -Method Head).Headers["cf-ray"]

If you see a cf-ray header (for example, cf-ray: 8a1b2c3d4e5f6g7h-SJC), your traffic is going through Cloudflare. If not, check your DNS configuration.

Verify DNS is pointing to Cloudflare

Your domain must resolve to Cloudflare IP addresses for traffic to be proxied:

dig +short www.example.com
Resolve-DnsName -Name www.example.com | Select-Object -ExpandProperty IPAddress

The returned IP addresses should be Cloudflare IPs ↗︎. If they point directly to your origin server, your DNS records are not proxied.

To fix this:

  1. Go to the Cloudflare dashboard ↗︎.
  2. Find the DNS record for the slow hostname.
  3. Ensure the Proxy status is set to Proxied (orange cloud icon).

Note

If your DNS records are set to DNS only (gray cloud), Cloudflare does not proxy the traffic and cannot improve performance. The request goes directly to your origin server.


Gather information about the slow requests

Performance issues are easier to solve when you can pinpoint exactly what is slow. Start by gathering the following information:

  1. Identify specific slow requests - Determine which URLs, assets, or API endpoints are slow.
  2. Measure the slowness - Quantify the delay (for example, "this image takes 5 seconds to load").
  3. Reproduce the issue - Confirm the slowness is consistent and not a one-time occurrence.

Step 1: Use Observatory to analyze performance

Observatory provides synthetic tests and real user monitoring (RUM) data to assess your website's performance.

RUM is particularly important as these metrics are captured from your actual visitors' browsers so it's an objective view of how they are experiencing your website, rather than lab data, which is typically more detailed (useful for debugging) but doesn't always correlate with the devices or connectivity real people have access to.

Run a speed test

  1. Go to the Cloudflare dashboard. Go to Observatory ↗
  2. Select Observatory.
  3. Enter the URL you want to test and select Run test.

Understand the results

Observatory reports key metrics:

Metric What it measures Target
Largest Contentful Paint (LCP) Time until the largest visible element loads Under 2.5 seconds
First Contentful Paint (FCP) Time until the first content appears Under 1.8 seconds
Cumulative Layout Shift (CLS) Visual stability during page load Under 0.1
Time to First Byte (TTFB) Time until the first byte of response is received Under 800 ms
Total Blocking Time (TBT) Time the main thread is blocked Under 200 ms

Real User Monitoring reports similar metrics but you'll see Interaction to Next Paint (INP) in place of Total Blocking Time (TBT).

TBT only measures during page load but INP measures every interaction real visitors make with your website.

Based on Observatory results, enable relevant Speed optimizations:

  • Brotli compression - Compress responses for faster transfer
  • Early Hints - Preload critical resources
  • HTTP/2 and HTTP/3 - Use modern protocols with multiplexing, allowing multiple requests over a single connection instead of opening separate connections for each asset
  • Image optimization - Automatically optimize and resize images
  • Rocket Loader - Defer loading of JavaScript to improve paint times

Step 2: Identify slow resources

If Observatory and RUM data point to specific issues, use browser developer tools to investigate individual resources.

Use browser developer tools

  1. Open your browser's developer tools.
  2. Go to the Network tab.
  3. Reload the page and observe which requests take the longest.
  4. Sort by Time or Duration to identify the slowest assets.
  5. Note the specific URLs of slow resources.

Look for:

  • Large images or videos
  • Slow API calls
  • Third-party scripts
  • Render-blocking resources

Step 3: Analyze origin performance

If your website is slow, the issue may be at your origin server. Use Origin Analytics to understand how your origin is performing.

Check origin response times

In the Cloudflare dashboard:

  1. Go to Speed > Origin Analytics. Go to Origin Analytics ↗
  2. Review the Origin Response Time metrics.
  3. Look for patterns in slow responses (specific paths, times of day, or geographic regions).

High origin response times indicate your origin server is struggling. Consider:

  • Upgrading your hosting plan
  • Optimizing database queries
  • Implementing server-side caching

For a full guide on available metrics, diagnostic flows, and how to interpret origin status codes, refer to Origin Analytics.

Check for slow Workers

If your zone has Cloudflare Workers deployed, they execute on every matching request and add to the total response time. A slow Worker is a common cause of high TTFB.

To check if Workers are affecting performance:

  1. Go to Workers & Pages in the Cloudflare dashboard.
  2. Review which Workers are deployed on your zone.
  3. Check the Analytics for each Worker to see execution times.
  4. Temporarily disable Workers to isolate whether they are causing the slowness.

If a Worker is slow, review its code for:

  • Slow external API calls or fetch() requests
  • Inefficient loops or data processing
  • Missing await statements causing sequential instead of parallel execution

Step 4: Measure performance from the command line

For detailed timing metrics on specific requests, use command-line tools to measure performance.

Basic performance test

curl -w "\n\nDNS Lookup: %{time_namelookup}s\nTCP Connect: %{time_connect}s\nTLS Handshake: %{time_appconnect}s\nTime to First Byte: %{time_starttransfer}s\nTotal Time: %{time_total}s\n" -o /dev/null -s https://www.example.com/slow-asset.jpg
$url = "https://www.example.com/slow-asset.jpg"
try {
    $timing = Measure-Command { 
        $response = Invoke-WebRequest -Uri $url -ErrorAction Stop
    }
    Write-Host "Total Time: $($timing.TotalSeconds)s"
    Write-Host "Status: $($response.StatusCode)"
} catch {
    if ($_.Exception.Response) {
        $statusCode = $_.Exception.Response.StatusCode.value__
        Write-Host "Error Status: $statusCode"
    }
    Write-Host "Error: $($_.Exception.Message)"
}

Example output (bash):

DNS Lookup: 0.025s
TCP Connect: 0.045s
TLS Handshake: 0.120s
Time to First Byte: 0.350s
Total Time: 1.250s

Understand the metrics

The curl timing breakdown shows the following:

Metric Description High value indicates
DNS Lookup Time to resolve the domain name DNS issues or slow resolver
TCP Connect Time to establish TCP connection Network latency or server distance
TLS Handshake Time to complete SSL/TLS negotiation Certificate chain issues or slow server
Time to First Byte (TTFB) Time until first response byte Slow origin processing
Total Time Complete request duration Large file size or slow transfer

Test from different locations

To test from different geographic locations, use online tools like:


Step 5: Investigate caching

Caching is one of the most effective ways to improve website performance. When content is cached, Cloudflare serves it directly from a data center close to your visitors, eliminating the round trip to your origin server. This can reduce response times from hundreds of milliseconds to just a few milliseconds.

Uncached content must travel from the visitor to Cloudflare, then to your origin server, and back again. Use Cache Analytics to understand your cache performance and identify opportunities to cache more content.

Check if assets are cached

Check the cache status of a specific asset:

curl -s -D- -o /dev/null https://www.example.com/asset.jpg | grep -i "cf-cache-status"
(Invoke-WebRequest -Uri "https://www.example.com/asset.jpg" -Method Head).Headers["cf-cache-status"]

Possible values:

Status Meaning Action
HIT Served from Cloudflare cache No action needed
MISS Not in cache, fetched from origin May need cache rules
DYNAMIC Not eligible for caching Create a cache rule if static
BYPASS Cache intentionally bypassed Review cache rules
EXPIRED Cached copy was stale Increase Edge TTL
REVALIDATED Cloudflare confirmed content is current Increase Edge TTL

For a complete list, refer to Cloudflare cache responses.

Use Cache Analytics

  1. Go to the Cloudflare dashboard. Go to Overview ↗
  2. Review the Cache Performance section.
  3. Filter by Cache status equals MISS or DYNAMIC to identify uncached content.

Investigate why static content is not cached

If static assets (images, CSS, JavaScript) show a cache status of DYNAMIC, BYPASS, or MISS, investigate the cause:

Symptom Likely cause Solution
DYNAMIC status Content type not in default file extensions Create a Cache Rule to cache the content
DYNAMIC status Origin sends Cache-Control: private or no-store Create a Cache Rule to override origin cache control
BYPASS status A Cache Rule is bypassing cache Review your Cache Rules configuration
MISS on every request Response includes Set-Cookie header Configure your origin to not set cookies on static assets, or use a Cache Rule to ignore cookies
MISS with query strings Different query strings create different cache entries Use a custom cache key to ignore or normalize query strings