# Self-hosted Canvas

> Endpoint overrides, issuer rules, platform signing-key requirements, and the launch-error recovery table for Canvas instances you run yourself.

[Canonical HTML page](https://safeonlineexam.com/docs/canvas/self-hosted-canvas)

The service is portable across Canvas environments, but a deployment configured
with Canvas **cloud** defaults will not work against a **self-hosted** Canvas until
you override its endpoints. Read this before registering a self-hosted instance.

## Why a self-hosted Canvas needs overrides [#why-a-self-hosted-canvas-needs-overrides]

The service has one active LTI platform configuration: `CANVAS_DOMAIN`,
`LTI_ISSUER`, `LTI_KEY_SET_URL`, `LTI_AUTH_URL`, `LTI_CLIENT_ID`, and
`LTI_DEPLOYMENT_ID`. Canvas cloud values are the defaults.

If a service still holding those defaults is registered in a self-hosted Canvas,
`/lti/login` redirects the embedded tool frame to `sso.canvaslms.com&#x60; — a page that
is intentionally not embeddable, so the browser shows &#x2A;*`sso.canvaslms.com refused
to connect`.**

## Set the endpoints [#set-the-endpoints]

For self-hosted Canvas, set its JWKS and authorization endpoints (normally under
`${CANVAS_DOMAIN}`):

```text
LTI_KEY_SET_URL=https://canvas.example.edu/api/lti/security/jwks
LTI_AUTH_URL=https://canvas.example.edu/api/lti/authorize_redirect
```

Set them in the release-bundle installer configuration for Cloud Run, or in the
Compose environment configuration for Docker. See
[Configuration → Canvas and LTI endpoints](/docs/deploy/configuration#canvas-and-lti-endpoints).

<Callout type="warn" title="Do not infer the issuer from the hostname">
  Set `LTI_ISSUER` to the **exact** `iss` value Canvas sends — not by guessing from
  `CANVAS_DOMAIN`. A self-hosted Canvas can still use
  `https://canvas.instructure.com` as its issuer. Keep the default unless the actual
  launch's `iss` differs.
</Callout>

## Platform signing keys [#platform-signing-keys]

Canvas must sign LTI launch tokens with an RSA key of **at least 2048 bits**. Before
registering a tool, inspect `${CANVAS_DOMAIN}/api/lti/security/jwks`. Do not use the
legacy sample JWKs shipped with some Canvas Docker configurations — a verifier must
reject undersized `RS256` keys rather than accept a weaker signature.

To rotate the three platform keys on a running Canvas, run this in the Canvas web
container and then restart the web service so its JWKS response reloads:

```bash
bundle exec rails runner '
  store = Lti::KeyStorage
  values = {
    CanvasSecurity::KeyStorage::PAST => CanvasSecurity::KeyStorage.new_key,
    CanvasSecurity::KeyStorage::PRESENT => CanvasSecurity::KeyStorage.new_key,
    CanvasSecurity::KeyStorage::FUTURE => CanvasSecurity::KeyStorage.new_key
  }
  store.send(:consul_proxy).set_keys(values, global: true)
  DynamicSettings.reset_cache!
'
```

After the restart, verify every key at `${CANVAS_DOMAIN}/api/lti/security/jwks` has
a 2048-bit-or-larger modulus, then reopen the external tool so Canvas issues a fresh
launch token. Rotating all three keys invalidates a launch already open in an
iframe — that is expected.

## Detector theme loading [#detector-theme-loading]

Self-hosted Canvas may also hit the local-file (HTTP 422) case when serving
uploaded theme JavaScript. Use the hosted theme loader described in
[Load the detector script](/docs/canvas/detector-script#self-hosted-canvas-the-local-file-http-422-case).

## Launch-error recovery [#launch-error-recovery]

Use the visible error to choose the next check:

| What appears in Canvas                 | Meaning                                                                                          | Recovery                                                                                                                                         |
| -------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `sso.canvaslms.com refused to connect` | The tool is using Canvas cloud's authorization endpoint for a self-hosted Canvas.                | Set `LTI_AUTH_URL` and `LTI_KEY_SET_URL` to the self-hosted endpoints, deploy, and relaunch.                                                     |
| `Canvas Signing-Key Error`             | Canvas's platform JWKS contains an RSA key smaller than 2048 bits.                               | Rotate the three `Lti::KeyStorage` keys above, restart Canvas web, verify the public JWKS, and reopen the tool.                                  |
| `Invalid LTI Launch`                   | The signed launch could not be verified for another reason.                                      | Reopen the tool. If it repeats, compare deployment ID, issuer, client ID, target-link URI, and public JWKS URL against the Canvas Developer Key. |
| `Connect Canvas`                       | The launch succeeded but the user has not granted Canvas API access to this Canvas instance yet. | Select **Connect Canvas** and complete the authorization flow.                                                                                   |

The full symptom index is in
[Troubleshooting → Canvas launch](/docs/troubleshooting/canvas-launch).