gighiveVideo files were not being cached by Cloudflare despite having proper Cache-Control headers set. Investigation revealed malformed Content-Range headers were preventing Cloudflare from caching the responses.
Apache configuration contained custom logic that attempted to manually set Content-Range headers for non-range requests:
# BROKEN CODE (removed)
Header set Accept-Ranges "bytes"
RewriteEngine On
RewriteCond %{HTTP:Range} !(^bytes=.*)
RewriteRule .* - [E=norange:1]
Header set Content-Range "bytes 0-%{CONTENT_LENGTH}e/%{CONTENT_LENGTH}e" env=norange
%{CONTENT_LENGTH}e wasn’t being populated, resulting in:
Content-Range: bytes 0-(null)/(null)
HTTP spec violation: Content-Range headers should only appear in 206 Partial Content responses (range requests), not in 200 OK responses (full file downloads)
Response headers for full file request:
HTTP/1.1 200 OK
Content-Length: 537519808
Content-Range: bytes 0-(null)/(null)
Cache-Control: public, max-age=604800, immutable
cf-cache-status: MISS
The (null) values indicate the variable substitution failed, creating an invalid header.
Response headers for full file request:
HTTP/1.1 200 OK
Content-Length: 347843014
Cache-Control: public, max-age=604800, immutable
cf-cache-status: HIT
No Content-Range header present (correct behavior for full file downloads).
Response headers for range request:
HTTP/1.1 206 Partial Content
Content-Range: bytes 131072-347974085/347974086
Content-Length: 347843014
Cache-Control: public, max-age=604800, immutable
cf-cache-status: HIT
Apache automatically generates proper Content-Range header with real byte positions.
Removed the problematic rewrite rules from /home/sodo/scripts/gighive/ansible/roles/docker/templates/apache2.conf.j2:
# CORRECT CONFIGURATION
<FilesMatch "\.(mp4|mov|au|mp3|wav|aac)$">
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "Range"
Header set Access-Control-Expose-Headers "Content-Length, Content-Range"
</FilesMatch>
Content-Range headersTesting revealed an additional constraint: Cloudflare Free plan has a 512 MB file size limit for caching.
| File Size | Content-Range Header | cf-cache-status | Cacheable |
|---|---|---|---|
| 348 MB | bytes 131072-347974085/347974086 |
HIT | ✅ Yes |
| 503 MB | bytes 0-503586071/503586072 |
HIT | ✅ Yes |
| 537 MB | bytes 71368704-537519807/537519808 |
MISS | ❌ No (over limit) |
| Plan | File Size Limit | Cost |
|---|---|---|
| Free | 512 MB | $0 |
| Pro | 5 GB | $20/month |
| Business | 5 GB | $200/month |
| Enterprise | Custom | Custom pricing |
Pros:
Cons:
Best for: Most users with typical video file sizes
Pros:
Cons:
Example:
# Trim video to get under 512 MB
ffmpeg -i large_video.mp4 -ss 00:01:00 -to 00:55:00 -c copy trimmed_video.mp4
Best for: Users with only a few oversized files
Pros:
Cons:
Best for: Professional deployments, high-traffic sites
Pros:
Cons:
Example structure:
video.m3u8 # Playlist file
segment_001.ts # 5 MB segment (cached)
segment_002.ts # 5 MB segment (cached)
segment_003.ts # 5 MB segment (cached)
...
Best for: Large video libraries, professional streaming applications
Pros:
Strategy:
Best for: Users wanting to optimize without major changes
Content-Range header bug fixedApache’s mod_headers and core functionality automatically:
Accept-Ranges: bytes headerRange: bytes=X-Y in requestsContent-Range)Content-Range)No manual configuration needed beyond CORS headers.
The removed code attempted to:
Content-Range headers for non-range requests (HTTP spec violation)%{CONTENT_LENGTH}e environment variable (not available in rewrite context)This was likely:
/home/sodo/scripts/gighive/ansible/roles/docker/templates/apache2.conf.j2