Iframes and Process Allocation
A common question I've encountered when consulting with teams that utilize <iframe>
in their web application
architecture is: Does an <iframe>
execute in parallel on a separate thread / in a dedicated process?
Web application process management is handled by the browser, and this can lead to interesting
and unexpected performance implications when using <iframe>
elements.
In this tip, I detail a few different considerations regarding how Chromium browsers group frames together into shared processes and how an iframe based web architecture may have unique performance considerations when compared to ordinary web applications.
Example: A Third Party Add-On / Extension
Many web applications are becoming Platforms that host other applications. Consider Microsoft Teams Apps, Stripe Apps, Slack Apps, Google Workspace Extensions, and so on. Platforms expose an Extension API and allow web developers to build custom extensions with access to certain privileged data, APIs, and authorization contexts.
For example, consider the Monday.com integration for Microsoft Teams or the DocuSign integration for Stripe
Platform/Host applications do not directly execute these third-party extensions and instead expose the APIs to the Extension (for example, allowing a hosted extension to read User or Message data from Microsoft Teams, or Invoice Details from Stripe).
This is usually modeled as a hosted <iframe>
and a postMessage
based API contract.
How does the browser facilitate this?
The answer is that it will vary depending on how the <iframe>
element is configured within a Host!
Chromium uses a combination of tab-based isolation and site-based isolation. Generally, this means:
- Each new browser tab will be granted a dedicated Renderer process (and Main Thread) to run the web application in the address bar
- Iframes within a browser tab, if they differ in site (i.e.,
microsoft.com
andmonday.com
differ in site), will run in separate Renderer processes - Iframes within a browser tab, if they share the same site (i.e.
dashboard.stripe.com
anddashboard.stripe.com/extension
share the same site ofstripe.com
) and will run in the same Renderer process
Definition of site
Chromium (as of writing) defines a site to use for process grouping of iframes by using eTLD+1. Consider the following table for examples:
Host | Iframe | Same Site? |
---|---|---|
www.microsoft.com | www.monday.com | ❌ microsoft.com is not monday.com |
teams.microsoft.com | loop.microsoft.com | ✅ microsoft.com |
dashboard.stripe.com | dashboard.stripe.com/extension | ✅ stripe.com |
dashboard.stripe.com | www.stripe.com | ✅ stripe.com |
Note: For all the examples above, I'm assuming they are all using the
https://
scheme. Scheme differences can also create differences in site, but most modern web applications are all onhttps
, so I am skipping these details. For more information see this article.
Let's look a bit more closely at a few examples!
The Host Application
A Host Application runs directly in the user's browser tab. (i.e., the Microsoft Teams, Slack, Stripe Dashboard, etc.). When a user opens the Host Application, the browser will create a new Renderer process.
If the user opens another browser tab to a separate instance of the Host application, the browser will allocate another renderer process.
In this scenario:
- The two Renderer processes may execute JavaScript code and update their respective DOM instances in parallel; they each have dedicated Main Threads
- The two Renderer processes may not share code execution or DOM (i.e. a
<script>
tag or Document Node in one tab does not become available to the other process)
Opening a Hosted extension with different site
Let's consider what happens when a user opens the third-party extension application iframe hosted on a different site. This is the model that the Microsoft Teams / Microsoft 365 Apps framework has taken.
<!-- Within teams.microsoft.com host -->
<iframe src="https://www.monday.com/teams_extension"></iframe>
Now, there's an iframe embedded in the page, pointing to a different site, hosting the third-party application.
How does the browser allocate processes for this case?
A Dedicated Renderer Process
When an iframe is opened within a web page of a different site, a new Renderer process must be created. This also creates a new Main Thread (and other threads) for that hosted extension to use for its isolated execution context.
This has interesting performance side-effects!
- The hosted extension does not share resources with the hosting process. This means it has its own threads to execute scripts, etc. and generally its execution should not impede the performance of the host application
- The hosted extension does not share memory with the hosting process (security feature). This also means that memory allocation within the hosted extension won't affect the host application. Running out of memory in the hosted extension will not degrade the host.
- Processes must communicate with each other through
postMessage
-- they cannot directly access each other's DOM
Note: You may see this case referred to as an OOPIF in Chromium source code. This stands for Out-of-Process Iframe.
Iframes + Tabs of Different Site
Let's expand our example and observe some other unique considerations. Users often have multiple tabs open at once, even at times to the same application.
Consider the case where the user opens two tabs to Microsoft Teams, and in each tab, the user opens the Monday.com extension iframe. Chromium browsers will actually re-use the existing Monday.com Renderer process to host the two Monday.com iframes, while keeping the Microsoft Teams applications in separate processes!
In general, this means that the Monday.com extensions are now competing for resources on the same threads and memory! For example:
- A Long Task in one iframe will affect the performance of the other
- JavaScript execution will interleave on the same Main Thread, despite different execution contexts and DOM instances
- Running out of memory in the one iframe will crash all iframes of the Monday.com extension hosted iframes across the browser
If your application is using a similar cross-origin iframe based approach, be aware that your hosted extension across the entire set of browser tabs will be competing for shared resources; you may want to throttle execution or release memory of inactive iframes.
It's important to note that although the Monday.com process is being re-used across each iframe, the browser is able to create separate
execution contexts for each instance of the <iframe>
within the same process. Execution contexts within a process will share resources
in the process (thread time and memory).
A Hosted extension on the same site
Let's consider a different hosted iframe architecture. In this architecture, hosted extensions are hosted on the same site as the host.
For example, these all share the same site of stripe.com
:
dashboard.stripe.com
www.stripe.com
dashboard.stripe.com/extension?extensionId=abc123
In fact, this is the model used by Stripe Apps! How does the browser manage processes in this case?
Sharing Sites between Iframe and Tab
If both the host and the iframe within a host share the same site, the browser will group their executions within the same process (even if the hosted iframe is sandboxed!).
This means that a host and a hosted extension within the same tab will:
- Compete for execution time on the same thread (i.e. a Long Task in one will degrade the other, as they interleave on the same thread)
- Share memory (i.e. running out of memory in the hosted extension will crash the host tab)
Although the browser will maintain separate execution contexts for the iframe and the host, the extension can directly degrade the performance of the host and vice-versa!
Same site considering multiple tabs
As mentioned previously, browsers will generate a new process for each browser tab, to host the execution for that site. In cases where there are multiple tabs, each hosting a same site iframe, the iframes will not be grouped into a shared process.
This means that iframes executing within a host of the same site compete with the host for thread time and memory resources, but not other iframes across the browser. Hosts do not share threads with each other because they are granted dedicated processes at the tab level.
Origin Agent Clusters
An available browser API called Origin-Agent Clusters can be used to signal to the browser that certain web applications would perform better with dedicated resourcing rather than being grouped by site.
It can be enabled by serving a HTTP header on your <iframe>
:
Origin-Agent-Cluster: ?1
This will instruct the browser to isolate an iframe based on its origin rather than using its site. What this means is that an iframe hosted within a same site can acquire its own resourcing and dedicated process based on its entire origin.
If the Origin-Agent-Cluster
header is enabled, consider the following table:
Host | Iframe | Same Site? | Same Origin? |
---|---|---|---|
www.microsoft.com | www.monday.com | ❌ microsoft.com is not monday.com | ❌ www.microsoft.com is not www.monday.com |
teams.microsoft.com | loop.microsoft.com | ✅ microsoft.com | ❌ teams.microsoft.com is not loop.microsoft.com |
dashboard.stripe.com | dashboard.stripe.com/extension | ✅ stripe.com | ✅ dashboard.stripe.com |
dashboard.stripe.com | www.stripe.com | ✅ stripe.com | ❌ dashboard.stripe.com is not www.stripe.com |
Anything that doesn't share a same-origin would then be allocated a dedicated process by the browser, regardless of if it shared a same-site.
Exploring for yourself
Chromium exposes tools for inspecting this process grouping! One option you can use is the browser task manager to see dedicated process allocation at a high level.
A more powerful option is the Chromium Frame Tree. You can access this by navigating to chrome://process-internals
and selecting
Frame Tree. Once in there, you can see site isolation keys and process IDs.
In the example below, an <iframe>
of substrate.office.com
is hosted within www.office.com
. These share the same site and they get grouped in the same process:
In another interesting example, chat.google.com
is served in an <iframe>
to mail.google.com
host, but utilizes the
Origin-Agent-Cluster: ?1
header. We can see in the Frame Tree it receives a dedicated process, and is marked as origin-keyed:
A note about other browsers and exceptions
Browsers all differ in implementation, and other browsers will group processes across tabs and iframes differently! For example, Safari (as of writing) places iframes of separate origin in the same process as their host but doesn't share across tab!
Chromium also has exceptions to the general behavior explained above, such as in memory constrained environments (like low-end Android devices).
There are other interesting exceptions across tabs regarding cases where an opening tab can access the opened tab and communicate synchronously.
Looking forward
From a security perspective, it's more secure to group sites per-origin rather than per site / eTLD+1. Chromium has an experimental setting strict-site-isolation
that can be enabled in chrome://flags
to showcase origin-based isolation:
I believe it's likely that the web will experience more cases of origin-based isolation as browsers evolve.
Conclusion
We explored how Chromium groups iframes and tabs into processes based on site, and how this may affect the performance of a web architecture; with iframes being used more frequently to facilitate micro-frontend and third-party extension host architectures on the web, this is particularly important.
Understanding these process fundamentals and trade-offs can help web developers decide on an architecture that best fits their requirements and can help understand bugs and performance degradations in complex frontend systems.
Some additional external resources can be found here:
- Chromium Spec on Site Isolation
- Origin-Agent Clusters
- Deprecation of
document.domain
- Same Site vs. Same Origin
A special thanks to Michael Zlatkovsky for our discussion which prompted the November 2023 clarifications to this article!
That's all for this tip! Thanks for reading! Discover more similar tips matching Browser Internals and CPU.