CDN and traffic management integration
Edit on GitHubWhen a CDN or traffic management solution (for example, Akamai, Cloudflare, or Fastly) sits in front of a Spryker application, HTTP response compression must be correctly coordinated between the CDN and the Spryker frontend container. Misconfiguration can result in responses leaving the infrastructure uncompressed, which significantly increases data transfer volume, response latency, and infrastructure overhead.
This document explains the request flow, how compression negotiation works, common pitfalls, and how to configure both sides for optimal performance.
Request flow with a CDN
When a CDN is deployed in front of a Spryker application, the request flow is as follows:
Client browser ──► CDN (Akamai/Cloudflare/...) ──► Load Balancer (ALB/NLB) ──► Frontend (Nginx) ──► Application (PHP-FPM)
- The client browser sends a request with
Accept-Encoding: gzip, deflate, br, indicating supported compression encodings. - The CDN terminates the client connection and makes an origin pull request to the load balancer. The
Accept-Encodingheader the CDN sends on origin pulls depends on the CDN configuration and may differ from the client’s original header. - The load balancer (AWS ALB/NLB) passes the request through to the frontend container. Load balancers do not compress responses.
- The Frontend (Nginx) container either serves a static asset or proxies the request to an application container (Yves, Zed, Glue, or others) through FastCGI. Based on the
Accept-Encodingheader received, the Frontend container compresses the response before sending it back.
The frontend (Nginx) container configured through the assets: section in the deploy file is responsible for compressing both static assets and dynamic application responses. This is the only compression point within the Spryker infrastructure. Load balancers (AWS ALB/NLB) do not compress responses. If the frontend container is not configured to allow compression, all responses leave the infrastructure uncompressed even if client (browser or CDN) supports compression, increasing data transfer volume over the public network between the origin and the CDN.
How compression negotiation works
HTTP compression relies on content negotiation between the client and the server using two headers:
Accept-Encoding(request header): the client tells the server which compression encodings it supports (for example,gzip,br,deflate).Content-Encoding(response header): the server tells the client which encoding was applied to the response body.
When a CDN sits between the client and the origin, there are two separate negotiation cycles:
- Client to CDN: the browser sends
Accept-Encoding: gzip, deflate, br. The CDN may serve a cached compressed response directly. - CDN to origin (origin pull): the CDN sends its own
Accept-Encodingheader to the origin. This header depends on the CDN’s configuration, not the client’s request.
Common pitfall: CDN does not request compression from origin
Many CDNs, by default, do not forward the client’s Accept-Encoding header to the origin, or they send a limited version. For example:
- A CDN might send
Accept-Encoding: gzipbut not includebr(Brotli). - A CDN might strip the
Accept-Encodingheader entirely on origin pulls.
In these scenarios, even if the Spryker frontend container has Brotli enabled, it never activates because no origin request asks for it. The CDN then receives an uncompressed response, compresses it itself (using gzip), and delivers it to the client. While the client receives a compressed response, the data between the origin and the CDN flows uncompressed, which means:
- All data flows uncompressed over the public network between the origin and the CDN edge, increasing data transfer volume and response latency.
- For typical e-commerce HTML pages, uncompressed responses can be 10-30x larger than compressed ones, significantly increasing infrastructure overhead.
- The CDN must spend CPU resources compressing every origin response.
A properly configured CDN mitigates the impact of uncompressed origin responses through two mechanisms:
- Caching: The CDN caches responses at the edge. Subsequent requests for the same page are served from the cache without an origin pull, so the uncompressed transfer only happens on cache misses or cache refreshes.
- Bot filtering: CDN-level bot management reduces the volume of origin pulls by filtering out unwanted bot traffic before it reaches the origin.
However, if the CDN is not configured to cache effectively (for example, low cache hit rates for dynamic pages) or does not filter bot traffic, the origin handles a high volume of requests. In this case, every origin pull transmits uncompressed data over the public network, which has the same effect as having no CDN at all for data transfer volume and latency. Properly configuring both compression and CDN caching and bot filtering is essential for optimal performance.
Configuring the CDN for optimal compression
Required: forward Accept-Encoding to origin
Configure the CDN to send Accept-Encoding: br, gzip, deflate on all origin pull requests. This lets the Spryker frontend container compress responses at the origin, reducing data transfer through the load balancer.
Akamai
In the Akamai Property Manager, configure the origin server behavior:
- In the Origin Server behavior, verify that the Allow Clients To Send setting includes
Accept-Encoding. - Add a Modify Outgoing Request Header rule that sets or appends the
Accept-Encodingheader:- Action: Modify
- Header Name:
Accept-Encoding - Header Value:
br, gzip, deflate
- In the Content Compression behavior, set Enable Compression to on and verify Brotli Support is enabled.
By default, Akamai may not include br in the Accept-Encoding header on origin pulls. This means Brotli compression configured in the Spryker frontend container does not activate, even though it is available. Explicitly configuring Akamai to request Brotli from the origin can improve compression ratios significantly compared to gzip alone.
Cloudflare
Cloudflare automatically handles compression between the client and Cloudflare’s edge. For origin-to-edge compression:
- In the Speed > Optimization settings, verify that compression is enabled.
- Cloudflare respects the origin’s
Content-Encodingheader. Make sure the Spryker frontend container has compression enabled (both gzip and Brotli) so that Cloudflare receives pre-compressed responses. - Under Network, review the request headers Cloudflare sends to the origin and confirm
Accept-Encodingincludesbr, gzip.
Other CDN providers
For other CDN or traffic management solutions, verify the following in the provider’s configuration:
- The
Accept-Encodingheader on origin requests includesbr, gzip, deflate. - The CDN does not strip or modify the
Content-Encodingresponse header from the origin. - The CDN does not re-compress already-compressed responses (double compression).
Required: configure Spryker compression
In the deploy file, enable both gzip and Brotli compression with static: true to compress both static assets and dynamic responses:
assets:
image: spryker/nginx-brotli:latest
mode: production
compression:
gzip:
static: true
level: 5
brotli:
static: true
level: 5
Setting static: only disables on-the-fly compression. With this setting, only pre-compressed static files (.gz, .br) are served compressed. Dynamic responses from PHP applications (HTML pages, API responses, JSON) are sent uncompressed. If a CDN is in front of the application, this means all dynamic content leaves the infrastructure uncompressed, increasing data transfer volume and response latency.
Use static: true to enable compression for both static and dynamic content.
For details on all compression options, see Deploy file reference - assets.
Verifying compression is working
Test origin compression directly
To verify that the Spryker frontend container compresses responses correctly, send requests directly to the load balancer, bypassing the CDN:
Test Brotli compression:
curl -s -o /dev/null \
-H "Accept-Encoding: br" \
-H "Host: www.your-shop.com" \
-D - https://your-alb-endpoint.region.elb.amazonaws.com/your-page | grep -i content-encoding
Test gzip compression:
curl -s -o /dev/null -w "Size: %{size_download} bytes\nContent-Encoding: " \
-H "Accept-Encoding: gzip" \
-H "Host: www.your-shop.com" \
-D - https://your-alb-endpoint.region.elb.amazonaws.com/your-page | grep -i content-encoding
Test without compression:
curl -s -o /dev/null -w "Size: %{size_download} bytes\n" \
-H "Accept-Encoding: identity" \
-H "Host: www.your-shop.com" \
https://your-alb-endpoint.region.elb.amazonaws.com/your-page
Expected results:
- With
Accept-Encoding: br, the response should includeContent-Encoding: brand be significantly smaller (typically 20-40x compression for HTML). - With
Accept-Encoding: gzip, the response should includeContent-Encoding: gzipand be approximately 7-10x smaller. - Without compression, the response is the full uncompressed size.
If the Brotli or gzip test returns the same size as the uncompressed test, compression is not active and needs to be configured.
Verify CDN-to-origin compression
Check your CDN’s analytics or logs to verify what Accept-Encoding header is sent on origin pulls and what Content-Encoding is received from the origin:
- Akamai: Use the Akamai Diagnostic Tools or enable origin debug headers.
- Cloudflare: Check the
cf-cache-statusand origin response headers in the dashboard.
Understanding compression impact on different response types
Not all response types benefit equally from compression. Understanding the traffic composition helps prioritize optimization:
| Response type | Typical compression ratio (gzip) | Typical compression ratio (Brotli) | Notes |
|---|---|---|---|
| HTML pages | 7-10x | 20-40x | Largest impact. HTML pages from Yves (product pages, category pages, search results) are typically the biggest contributor to data transfer volume. |
| JSON/API responses | 5-10x | 10-20x | Glue API responses and AJAX calls benefit significantly. |
| CSS/JS (pre-built) | 3-5x | 4-7x | Usually served from pre-compressed files with static: true or static: only. |
| Images (JPEG, PNG, WebP) | 1x (no benefit) | 1x (no benefit) | Already compressed formats. Do not apply HTTP compression to images. |
| SVG | 3-5x | 5-10x | XML-based, compresses well. |
In typical e-commerce deployments, dynamically generated HTML pages account for the vast majority of response data volume, while static assets (JS, CSS, images) contribute a relatively small share. This is because static assets are often cached by the CDN after the first request, while HTML pages are generated on each origin pull. Ensuring compression is active for dynamic responses is critical for reducing data transfer overhead and response latency.
Best practices summary
| Practice | Description |
|---|---|
| Enable both gzip and Brotli | Configure both engines with static: true in the deploy file. Use spryker/nginx-brotli:latest image. |
Use static: true, not static: only |
Ensure dynamic responses are compressed on the fly, not only pre-compressed static files. |
Configure CDN to send Accept-Encoding: br, gzip, deflate |
Verify the CDN sends compression headers on origin pulls. Do not assume the client’s headers are forwarded. |
| Verify compression with direct ALB tests | Bypass the CDN and test compression directly against the load balancer to confirm nginx is compressing. |
| Prefer Brotli over gzip when possible | Brotli provides significantly better compression ratios, especially for HTML content, reducing data transfer volume further. |
| Review CDN cache behavior | Ensure the CDN caches compressed variants correctly. The Vary: Accept-Encoding header (automatically added by nginx) is required for correct caching. |
Thank you!
For submitting the form