Why Your LTI® Tool Shows a Blank Page
The short answer
An empty iframe means the browser got something and rendered nothing. There are four likely causes: the browser refused to frame your page, the launch failed before your application ran, the launch succeeded but your tool could not read its own session, or your application errored after launch. The network tab separates them in a couple of minutes.
Set up first
Before launching again, open developer tools and enable "preserve log" in the network tab.
An LTI® launch is several redirects. Without preserve log, the browser clears the log on each navigation and the failing request — usually not the last one — is gone before you can look at it. This one setting is the difference between a five-minute diagnosis and an afternoon.
Then launch, and work down this list.
1. The browser refused to frame your page
Check: the console. A refusal is loud — Refused to display ... in a frame,
or the LMS showing "refused to connect".
Cause: your server sent
X-Frame-Options
set to DENY or SAMEORIGIN, or a Content-Security-Policy with a
frame-ancestors
directive that does not include the LMS. Many frameworks and security middlewares set one of these by default,
which is correct for an ordinary web application and wrong for an LTI® tool.
Fix: allow framing on your launch route. X-Frame-Options cannot express
"these origins", so the modern approach is to drop it and use
Content-Security-Policy: frame-ancestors with the platforms you serve. If you
support many institutions, that list is dynamic — which is a good reason to keep
the launch endpoint separate from the rest of your application.
This is the most common cause on a first integration, and it never appears in local testing because nothing is framing you there.
2. The launch failed before your application ran
Check: the network tab, for a non-2xx response on the launch POST. Look at the response body — a JSON error is usually sitting there unrendered.
Cause: the launch was rejected during verification. Typical reasons: the platform is not registered, the issuer does not match, the signature failed, the key set could not be fetched, or the token was expired or replayed.
Fix: depends on the error, and the error names it. If you use LTIAAS, every code is documented at the error message reference, which maps each to its cause and where it gets fixed.
Two that produce particular confusion:
- A token that "can't be used before" a time in the future. The platform's server clock has drifted. Nothing is wrong with your integration.
- A key set that cannot be fetched. Every launch from that platform fails at once, which looks like a total outage and is a URL problem.
3. The launch worked but the tool lost its state
Check: does it work in a normal Chrome window and fail in Safari or Incognito? That pattern is close to diagnostic.
Cause: the tool wrote its launch state somewhere the browser then discarded,
so the state check at the end of the launch had nothing to compare against.
Errors mentioning invalid or missing state belong here.
Fix: the third-party cookie article covers this properly. Briefly: hold the state where the browser cannot drop it, using Platform Storage, or carry it in the URL.
This is the cause that produces the report "it works for everyone except one customer".
4. Your application errored after a successful launch
Check: did the launch POST return 200 and your application then render nothing? Then the LTI® part is done and this is an ordinary application bug — worth knowing, because it means you can stop looking at the protocol.
Common versions:
- The launch data was missing a field you required. Platforms are not obliged
to send a name or an email, and many are configured not to. An account-creation
step that requires an email address fails at exactly those institutions. Treat
everything except the issuer and
subas optional. - Your own session cookie was dropped, so your application redirected to a login page that itself will not frame — which loops back to cause 1.
- Your front end threw. The console will say so.
The quick triage
| What you see | Likely cause | Where to look |
|---|---|---|
| Console: refused to display in a frame | Framing headers | Your response headers |
| Launch POST returns 4xx | Launch verification failed | The response body |
| Fails in Safari or Incognito only | Storage blocked | Third-party cookies |
| Launch returns 200, page still empty | Your application | Your logs |
| Fails at one institution only | Registration or missing claims | That platform's registration |
If you need to raise it with someone
Whether that is us or another vendor, three things turn a slow thread into a quick one: a HAR file of the whole launch with preserve log on, the full error response rather than a screenshot of the message, and which platform and course it happened in. The HAR carries the redirect chain, which is where the answer usually is.
Common questions
Why does my LTI® tool show a blank iframe?
Usually one of four things — the browser refused to frame your page, the launch failed server-side and returned an error page with no body, the tool lost its launch state because storage was blocked, or your application errored after a successful launch. The network tab tells them apart quickly.
What does "refused to connect" mean in an LMS?
Your server sent a header telling the browser not to display the page in a frame — either X-Frame-Options, or a Content-Security-Policy frame-ancestors directive. The LMS is obeying it. The fix is on your side, in your response headers.
Why does it work for me but not for one customer?
Most often the browser. Safari and Incognito windows block third-party storage more aggressively, so a launch that depends on it fails only for the users on them.
Where do I start debugging?
Open the network tab with "preserve log" enabled before launching. The launch is several redirects, and without preserve log the failing one disappears before you can read it.
Next
- Why LTI® launches break without third-party cookies
- Error message reference — every LTIAAS error code
- The LTI® 1.3 security model — what the launch is verifying
