Serving Static Content with Cloud Storage? Don't Forget the CDN!
Many websites use cloud storage as part of how they deliver static content to end users. However using these services without a CDN in front of them has the potential to negatively impact performance. You might be surprised by how many sites do just that - about 8.5% of websites that use a CDN for their primary content, based on HTTP Archive data from January 2026!
Background
Content Delivery Networks (CDNs) are an essential component of website delivery, especially when it comes to web performance. According to the 2025 Web Almanac, approximately 70% of popular websites use them. CDNs enable users to connect to servers that are geographically close to them (and at lower latencies), provide distributed caching to offload backend/origin servers and provide other services such as image optimization, edge compute and security. Some CDNs offer cloud storage services, but pretty much all of them can sit in front of another cloud provider’s storage when it is configured for web delivery.

Many websites with a cloud backend/origin host their static content on cloud storage services such as Google Cloud Storage, Amazon S3, Oracle Cloud ObjectStorage and Azure Blob Storage. However it’s important to understand that these services operate as backends and do not automatically provide CDN functionality. Put another way: your content is only behind a CDN if you configure it to be!
In numerous web performance audits over the years, I’ve found cloud storage hostnames being used to deliver static content to end users. During my Performance Mistakes talk in November 2024, I shared that there were 580K websites serving content directly from Amazon S3. This has the potential to negatively impact performance, since those resources are generally served from the locations they are hosted in and not a CDN.
I queried the HTTP Archive to see how common this still is and found a few surprising statistics -
- Overall 5.8% of websites serve at least 1 request directly from cloud storage!
- 8.53% of websites that use a CDN for delivering their primary content, serve at least 1 request directly from cloud storage!
- The number of websites serving content directly from Amazon S3 is now 629K - which is an increase of 8.5% since November 2024!

Cloud Storage is not distributed like CDNs
When you configure cloud storage services, you usually have to define which region your content will be hosted in. This is essentially where your static content will live. When you deliver that content directly via a cloud storage solution, then it will fetch the asset the same as it would if it was hosted on a webserver.
For example, below is a request that was delivered from a European news site. I’m browsing it from the northeast US. The hostname s3.eu-central-1.amazonaws.com indicates that this content is being delivered from Amazon’s S3 region in Frankfurt, and I can confirm the same via a traceroute.
When I examine this in Chrome DevTools, I can see very high TCP and TLS connection times for this request!

What type of content is delivered directly via Cloud Storage
In this analysis, I’ve searched for 4 different popular cloud storage providers based on their documented URL structures for web delivery. For Amazon S3 I’m looking for hostnames ending in amazonaws.com with s3 optionally within the hostname. For Google Cloud Storage, I’m looking for a subdomain of storage.googleapis.com. For Azure, a subdomain of windows.net and for Oracle a subdomain of either oraclecloud.com or customer-oci.com.
The graph below illustrates the amount of requests delivered by these services across all measured sites, and it’s grouped by content type. Amazon S3 is by far the most commonly used cloud storage service when it comes to this type of delivery, followed by Google Cloud Storage.

If we look at the same data by the percentage of requests to each cloud storage service, we can see that regardless of the service almost 75% of content delivered from cloud storage are scripts and images. This type of content is best served to users via a CDN rather than a centralized storage solution.
Delivery of JSON content is also common. While JSON can be dynamically generated, if it’s being served from a cloud storage then it is likely cacheable. HTML delivery is less common, except for Oracle Cloud where it represents 11.8% of cloud storage requests!

Caching and Compression
One thing that may not be apparent when configuring cloud storage for delivery of web content is that compression and downstream caching are often not enabled by default.
Based on the HTTP Archive, a majority of compressible content types are not being served compressed when delivered directly from Cloud Storage. This is a critical performance mistake, and is often overlooked because most web servers and CDNs do this by default!

Most content being delivered from cloud storage should be cacheable unless personalized. However the percentage of cloud storage services including Cache-Control headers is incredibly low (with the exception of Google Cloud Storage). That means that clients will have to frequently make requests to these cloud storage services.
| Service | Cacheable Requests | Non-Cacheable Requests | % Cacheable | % Non Cacheable |
| Amazon S3 | 736,950 | 2,587,320 | 22.2% | 77.8% |
| Google Cloud Storage | 969,660 | 259,629 | 78.9% | 21.1% |
| Azure Blob Storage | 102,857 | 376,291 | 21.5% | 78.5% |
| Oracle Cloud Object Storage | 6,763 | 27,877 | 19.5% | 80.5% |
Digging a bit deeper we can see that images, JS, JSON and CSS account for most of the non-cacheable content delivered via Amazon S3. These asset types are often delivered with cache-control headers allowing caching from Google Cloud Storage.
| Amazon S3 | Google Cloud Storage | Azure Blob Storage | Oracle Cloud Object Storage | |||||
| type | cacheable | not-cacheable | cacheable | not-cacheable | cacheable | not-cacheable | cacheable | not-cacheable |
| image | 561,592 | 1,743,876 | 489,387 | 131,988 | 84,912 | 284,536 | 4,668 | 18,132 |
| script | 91,890 | 272,434 | 370,127 | 45,621 | 5,378 | 26,061 | 1,658 | 2,599 |
| json | 41,046 | 201,543 | 48,534 | 27,917 | 303 | 6,083 | 263 | 1,126 |
| css | 26,926 | 103,007 | 30,893 | 1,340 | 2,919 | 18,666 | 141 | 863 |
| xml | 40 | 85,778 | 193 | 11,909 | 9 | 9,186 | 33 | |
| other | 2,111 | 82,043 | 4,623 | 2,170 | 408 | 14,539 | 903 | |
| font | 13,002 | 33,652 | 24,619 | 1,344 | 8,242 | 8,511 | 30 | 31 |
| video | 191 | 37,699 | 441 | 19,435 | 680 | 5,183 | 79 | |
| text | 148 | 16,395 | 386 | 4,245 | 2 | 2,840 | 37 | |
| html | 4,350 | 20 | 10,382 | 541 | 4,071 | |||
| audio | 3 | 6,514 | 431 | 3,278 | 4 | 138 | 3 | 3 |
| wasm | 1 | 29 | 6 | 7 | ||||
Here’s an example from a popular movie theater chain in the US. This content appears to be loaded by a 3rd party (Unbounce), and that third party is configured to load images directly from S3. There are no cache headers present, which means that the browser will heuristically cache the resources. On this particular page, this third party’s S3 content accounted for over 9MB of content - none of them containing a cache-control header! Beyond that there are a few opportunities for image optimization that could be applied.

In an example from another movie theater’s website, we can see render-blocking CSS and JS loaded from Amazon S3, with no cache-control header to indicate caching, and no compression. This is perhaps the worst case scenario - where you have content critical to the rendering of your website that is loaded slowly from a centralized location, with unnecessary large payloads and then unpredictable caching (or no caching) due to a missing cache-control header.

Cloud Storage vs CDN Delivery Costs
It’s also worth evaluating how much delivering traffic directly from these cloud storage solutions is costing. Depending on your contracts with the providers, you may find that delivering directly via cloud storage is more expensive compared to CDN delivery - especially if you are not caching or compressing the content! If that is the case, you can get a double-win by saving money while improving performance!
Conclusion
It’s incredible that 8.5% of websites that utilize a CDN are delivering content to users directly from cloud storage services. Discovering and fixing these could provide a quick performance boost. When you audit your website’s performance, if you notice cloud storage hostnames then you should definitely investigate how they got there, and move that content behind your CDNs.
HTTP Archive queries
This section provides some details on how this analysis was performed, including SQL queries. Please be warned that some of the SQL queries process a significant amount of bytes - which can be very expensive to run. What Percentage of CDN delivered sites have request for Cloud Storage hosted assets
This query counts the number of sites that contain at least 1 request for an asset delivered directly via a Cloud Storage service (without a CDN).
SELECT
IF(JSON_VALUE(p.summary.cdn) IS NOT NULL AND NOT JSON_VALUE(p.summary.cdn) = "",true, false) AS usesCDN,
CASE
WHEN NET.HOST(url) LIKE "%.amazonaws.com" THEN "Amazon S3"
WHEN NET.HOST(url) LIKE "%storage.googleapis.com" THEN "Google Cloud Storage"
WHEN NET.HOST(url) LIKE "%.windows.net" THEN "Azure Blob Storage"
WHEN NET.HOST(url) LIKE "%.oraclecloud.com" THEN "Oracle Cloud Object Storage"
WHEN NET.HOST(url) LIKE "%.customer-oci.com" THEN "Oracle Cloud Object Storage"
ELSE "Unknown"
END AS CloudStorage,
COUNT(DISTINCT p.page) AS sites
FROM `httparchive.crawl.requests` AS r
INNER JOIN `httparchive.crawl.pages` AS p
ON r.page = p.page
WHERE
p.date = "2026-01-01" AND r.date = "2026-01-01"
AND p.client = "mobile" AND r.client = "mobile"
AND p.is_root_page = TRUE AND r.is_root_page = TRUE
AND (
NET.HOST(url) LIKE "%.amazonaws.com"
OR NET.HOST(url) LIKE "%storage.googleapis.com"
OR NET.HOST(url) LIKE "%.windows.net"
OR NET.HOST(url) LIKE "%.oraclecloud.com"
OR NET.HOST(url) LIKE "%.customer-oci.com"
)
GROUP BY 1,2
Caching and Compression of Cloud Storage Delivered Assets
This query counts the number of requests being delivered cached and compressed by content type.
SELECT
IF(JSON_VALUE(p.summary.cdn) IS NOT NULL AND NOT JSON_VALUE(p.summary.cdn) = "",true, false) AS usesCDN,
CASE
WHEN NET.HOST(url) LIKE "%.amazonaws.com" THEN "Amazon S3"
WHEN NET.HOST(url) LIKE "%storage.googleapis.com" THEN "Google Cloud Storage"
WHEN NET.HOST(url) LIKE "%.windows.net" THEN "Azure Blob Storage"
WHEN NET.HOST(url) LIKE "%.oraclecloud.com" THEN "Oracle Cloud Object Storage"
WHEN NET.HOST(url) LIKE "%.customer-oci.com" THEN "Oracle Cloud Object Storage"
ELSE "Unknown"
END AS CloudStorage,
JSON_VALUE(r.summary.type) AS type,
CASE JSON_VALUE(r.payload._contentEncoding)
WHEN 'gzip' THEN 'Gzip'
WHEN 'br' THEN 'Brotli'
WHEN 'zstd' THEN 'zStandard'
ELSE 'No compression'
END AS compression_type,
IF(SAFE_CAST(JSON_VALUE(r.payload._cache_time) AS INT64) > 0, "cacheable", "not-cacheable") AS cacheable,
COUNT(DISTINCT p.page) AS sites,
COUNT(*) AS requests
FROM `httparchive.crawl.requests` AS r
INNER JOIN `httparchive.crawl.pages` AS p
ON r.page = p.page
WHERE
p.date = "2026-01-01" AND r.date = "2026-01-01"
AND p.client = "mobile" AND r.client = "mobile"
AND p.is_root_page = TRUE AND r.is_root_page = TRUE
AND (
NET.HOST(url) LIKE "%.amazonaws.com"
OR NET.HOST(url) LIKE "%storage.googleapis.com"
OR NET.HOST(url) LIKE "%.windows.net"
OR NET.HOST(url) LIKE "%.oraclecloud.com"
OR NET.HOST(url) LIKE "%.customer-oci.com"
)
GROUP BY 1,2,3,4,5
Examples of Sites that load content from cloud storage services
Detailed examples of sites that are loading content from cloud storage services.
SELECT
p.rank,
r.page AS page,
JSON_VALUE(p.summary.cdn) AS pageCDN,
NET.HOST(r.url) AS hostname,
COUNT(*) AS requests,
SUM(CAST(JSON_VALUE(r.summary.respBodySize) AS INT64)/1024/1024) AS responseMB,
STRING_AGG(DISTINCT JSON_VALUE(r.summary.type)) AS types,
FROM `httparchive.crawl.requests` AS r
INNER JOIN `httparchive.crawl.pages` AS p
ON r.page = p.page
WHERE
p.date = "2026-01-01" AND r.date = "2026-01-01"
AND p.client = "mobile" AND r.client = "mobile"
AND p.is_root_page = TRUE AND r.is_root_page = TRUE
AND p.rank <= 1000 AND r.rank <= 1000
AND (
NET.HOST(url) LIKE "%.amazonaws.com"
OR NET.HOST(url) LIKE "%storage.googleapis.com"
OR NET.HOST(url) LIKE "%.windows.net"
OR NET.HOST(url) LIKE "%.oraclecloud.com"
OR NET.HOST(url) LIKE "%.customer-oci.com"
)
GROUP BY 1,2,3,4
ORDER BY 6 DESC