CDNs introduce caching layers between your server and Apple's AASA validation. When configured incorrectly, CDNs can serve stale, incorrect, or blocked AASA files, breaking Universal Links silently. This article covers how Apple fetches and caches AASA files, how popular CDNs interact with this process, and how to configure each one correctly.
For AASA file setup, see AASA file setup. For debugging AASA issues, see debugging AASA file issues. For iOS troubleshooting, see the iOS troubleshooting guide.
How Apple Fetches AASA Files
The Two-Layer Cache
When a user installs or updates your app, iOS fetches the AASA file through two layers:
- Apple's CDN (
app-site-association.cdn-apple.com): Apple caches AASA files on its own CDN. iOS devices fetch from Apple's CDN, not directly from your server. - Your CDN: Apple's servers fetch the AASA file from your domain, which may be served through your CDN (Cloudflare, Fastly, AWS CloudFront, etc.).
iOS device → Apple CDN → Your CDN → Your origin server
Both layers can cache stale data:
| Layer | Cache Duration | You Can Control? |
|---|---|---|
| Apple's CDN | 24-48 hours (typically) | No |
| Your CDN | Depends on your configuration | Yes |
When Apple Fetches
Apple fetches the AASA file:
- When the app is installed.
- When the app is updated.
- Periodically in the background (timing varies by iOS version).
Apple does not fetch on every link tap. The AASA file is cached on the device.
CDN-Specific Configuration
Cloudflare
Cloudflare is the most common CDN to cause AASA issues. Problems occur because:
- Bot protection: Cloudflare's bot detection can block Apple's AASA fetcher.
- Caching: Cloudflare caches the file, but cache purges may not propagate immediately.
- Page Rules: Rules that redirect or block requests can interfere.
Configuration:
Create a Page Rule or Configuration Rule for the AASA path:
URL: yourdomain.com/.well-known/apple-app-site-association
Settings:
- Cache Level: Bypass (or set short TTL)
- Security Level: Essentially Off
- Bot Fight Mode: Off for this path
- Browser Integrity Check: Off
Alternatively, add a Transform Rule to ensure the correct Content-Type:
When: URI Path equals "/.well-known/apple-app-site-association"
Then: Set response header Content-Type to "application/json"
Cloudflare Workers (advanced):
addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (url.pathname === '/.well-known/apple-app-site-association') {
event.respondWith(handleAASA(event.request));
}
});
async function handleAASA(request) {
const response = await fetch(request);
const newResponse = new Response(response.body, response);
newResponse.headers.set('Content-Type', 'application/json');
newResponse.headers.set('Cache-Control', 'public, max-age=3600');
return newResponse;
}
AWS CloudFront
CloudFront configuration for AASA files:
- Origin: Point to your origin server (S3, EC2, etc.).
- Behavior: Create a cache behavior for
/.well-known/apple-app-site-association. - TTL: Set a short TTL (1 hour) so changes propagate quickly.
- Headers: Forward the
Acceptheader to the origin.
CloudFront cache policy:
| Setting | Value |
|---|---|
| Minimum TTL | 0 |
| Maximum TTL | 3600 (1 hour) |
| Default TTL | 3600 |
| Header forwarding | None (or Accept) |
Fastly
Fastly VCL configuration:
sub vcl_recv {
if (req.url == "/.well-known/apple-app-site-association") {
set req.http.X-Pass = "true";
}
}
sub vcl_fetch {
if (req.http.X-Pass == "true") {
set beresp.ttl = 1h;
set beresp.http.Content-Type = "application/json";
}
}
Vercel
Vercel serves files from the public/ directory. Place the AASA file at:
public/.well-known/apple-app-site-association
Add a vercel.json header configuration:
{
"headers": [
{
"source": "/.well-known/apple-app-site-association",
"headers": [
{ "key": "Content-Type", "value": "application/json" },
{ "key": "Cache-Control", "value": "public, max-age=3600" }
]
}
]
}
Nginx (as a caching reverse proxy)
If you use Nginx as a caching layer:
location = /.well-known/apple-app-site-association {
proxy_pass http://upstream;
proxy_cache_valid 200 1h;
add_header Content-Type application/json;
add_header Cache-Control "public, max-age=3600";
}
Cache Invalidation Strategies
When you update your AASA file, you need to invalidate caches at multiple levels:
Your CDN Cache
| CDN | Invalidation Method |
|---|---|
| Cloudflare | Purge cache for the specific URL in the dashboard or API |
| AWS CloudFront | Create an invalidation for /.well-known/apple-app-site-association |
| Fastly | Purge by URL via dashboard or API |
| Vercel | Redeploy (cache is purged automatically) |
Apple's CDN Cache
You cannot directly invalidate Apple's cache. Options:
- Wait: Apple refreshes its cache within 24-48 hours.
- Device-level refresh: Delete and reinstall the app on test devices.
- Developer mode: Use
applinks:yourdomain.com?mode=developerin your entitlement during development. This bypasses Apple's CDN and fetches directly from your server.
Verification
After updating and purging:
# Check your CDN
curl -I https://yourdomain.com/.well-known/apple-app-site-association
# Check Apple's cache
curl https://app-site-association.cdn-apple.com/a/v1/yourdomain.com
If your CDN returns the new version but Apple's CDN does not, Apple has not refreshed yet.
Common CDN-Related Failures
| Failure | CDN Cause | Fix |
|---|---|---|
| Apple cannot fetch AASA | Bot protection blocks Apple's fetcher | Whitelist Apple's user agents or disable bot protection for the AASA path |
| AASA returns HTML instead of JSON | CDN serves a custom error page | Ensure the origin returns JSON and the CDN forwards it |
| AASA returns 301/302 | CDN redirects (e.g., HTTP to HTTPS, www to non-www) | Apple does not follow redirects; serve AASA directly |
| Stale AASA after update | CDN cache not purged | Purge CDN cache and wait for Apple's CDN refresh |
| AASA returns with wrong Content-Type | CDN overrides Content-Type header | Set Content-Type: application/json in CDN configuration |
| Intermittent failures | CDN edge nodes serving different versions | Purge all edge locations, not just one region |
Testing CDN Configuration
Verify from Multiple Locations
Use a tool like curl with different DNS resolvers, or use a service like KeyCDN Tools to test from multiple geographic locations.
Monitor AASA Availability
Set up monitoring to alert you if the AASA file becomes inaccessible:
# Simple monitoring script
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
https://yourdomain.com/.well-known/apple-app-site-association)
if [ "$HTTP_STATUS" != "200" ]; then
echo "ALERT: AASA file returned HTTP $HTTP_STATUS"
fi
Run this check every 5-15 minutes. An inaccessible AASA file will not break Universal Links immediately (iOS caches the file on-device), but it will prevent new installs from configuring Universal Links.
Tolinku for AASA Hosting
Tolinku hosts your AASA file on infrastructure optimized for Apple's validation process. The platform handles Content-Type headers, avoids redirects, and serves the file from a reliable CDN with proper cache headers. This eliminates CDN-related AASA failures entirely. See the Universal Links developer guide for configuration.
For domain association, see universal links domain association. For the complete Universal Links guide, see universal links: everything you need to know.
Get deep linking tips in your inbox
One email per week. No spam.