Resource Timing API: Unlocking Cross-Origin Performance Data
Fix missing performance data for cross-origin assets. Learn how to use the Timing-Allow-Origin header to get accurate latency metrics for your web app.
We’ve all been there: staring at a performance dashboard, seeing massive gaps in our data for third-party scripts or CDN-hosted assets. You open up the browser console, try to inspect the PerformanceResourceTiming objects, and realize all the useful fields—like connectStart, requestStart, and responseEnd—are sitting at a flat zero. It's frustrating because you know the assets aren't loading instantaneously; the browser is just hiding the data from you for security reasons.
If you’re trying to optimize Core Web Vitals or track latency for assets hosted on a different domain, that "zeroed-out" data is a brick wall. The browser blocks detailed timing information by default to prevent timing attacks where a malicious site might infer information about a user's cross-origin requests.
Solving the Visibility Gap with Timing-Allow-Origin
The fix isn't as complex as you might think, but it requires coordination between your frontend and your server configurations. To get access to those detailed metrics, you need to implement the Timing-Allow-Origin (TAO) header on the cross-origin server.
When a browser makes a request for a resource, it checks if the server explicitly allows the origin to see its timing data. If the server sends Timing-Allow-Origin: https://your-site.com, the browser unlocks the full timing details for that specific resource in the Resource Timing API.
I once spent about two days debugging a slow-loading font file served from an external CDN. I had configured my Performance Observer API logic, but the numbers didn't make sense until I realized the CDN wasn't sending the TAO header. Once we added the header, we saw that the domainLookup time was significantly higher than expected, pointing us straight to a DNS configuration issue.
Implementing TAO for Better Web Performance
You shouldn't just open the floodgates to every origin. If you have a CDN, you can configure it to return the header dynamically based on the Origin request header, or specify your production domain directly.
Here is how you might configure this in an Nginx environment:
NGINX# Add this to your location block for assets add_header Timing-Allow-Origin "https://your-production-site.com";
Once this header is in place, your frontend code can finally access the data that was previously hidden. Here is a quick way to verify the data is flowing:
JAVASCRIPTconst observer = new PerformanceObserver((list) => { list.getEntries().forEach((entry) => { if (entry.initiatorType === CE9178">'fetch' || entry.initiatorType === CE9178">'xmlhttprequest') { // These values will be non-zero now! console.log(CE9178">`${entry.name}: ${entry.responseStart - entry.requestStart}ms`); } }); }); observer.observe({ entryTypes: [CE9178">'resource'] });
Why Cross-Origin Resource Sharing Matters for Latency
If you aren't tracking your Cross-Origin Resource Sharing (CORS) assets effectively, you’re flying blind. Many modern applications rely heavily on micro-frontends or distributed asset delivery. If those pieces are slow, your overall Web Performance suffers, and your users will feel it in the form of layout shifts or delayed interactivity.
| Metric | Without TAO | With TAO |
|---|---|---|
startTime | Available | Available |
domainLookupStart | 0 | Accurate |
connectStart | 0 | Accurate |
responseEnd | 0 | Accurate |
Common Pitfalls and Real-World Lessons
We initially tried to set Timing-Allow-Origin: * across our entire CDN, thinking it would be the easiest way to gather data. It worked, but it’s rarely the right move for security-sensitive applications. If your API returns sensitive information in the response headers, you should be much more restrictive with your origin list.
Also, remember that TAO only exposes timing data—it doesn't grant read access to the response body. That’s still handled by your standard CORS policy (Access-Control-Allow-Origin). Don't confuse the two; they solve different problems.
I’m still experimenting with how to best integrate this into our automated performance budgets. It’s one thing to see the data in the console, but it’s another to alert on it when it drifts. If you're building out your own monitoring, start by ensuring your primary assets are reporting correctly. Don't worry about every tiny icon file—focus on the large scripts and stylesheets that actually impact your Core Web Vitals.
What I’d do differently next time? I’d automate the verification of these headers in our CI/CD pipeline. It’s too easy for a configuration change on the CDN to strip these headers without anyone noticing until the next major performance audit.
FAQ
Does Timing-Allow-Origin affect my site's security? No. It only exposes timing information (when a request started, how long it took to connect, etc.). It does not allow the browser to read the content of the response.
Can I use multiple origins in the header?
No, the header only supports a single origin or the wildcard *. You'll need to configure your server to dynamically echo the requesting origin if you have multiple trusted domains.
What happens if I don't set this header?
The browser will return 0 for most timing properties in the PerformanceResourceTiming interface, making it impossible to diagnose latency issues for those assets.