Self-hosted Canvas
Endpoint overrides, issuer rules, platform signing-key requirements, and the launch-error recovery table for Canvas instances you run yourself.
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
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 — a page that
is intentionally not embeddable, so the browser shows sso.canvaslms.com refused to connect.
Set the endpoints
For self-hosted Canvas, set its JWKS and authorization endpoints (normally under
${CANVAS_DOMAIN}):
LTI_KEY_SET_URL=https://canvas.example.edu/api/lti/security/jwks
LTI_AUTH_URL=https://canvas.example.edu/api/lti/authorize_redirectOn Google Cloud these are _LTI_KEY_SET_URL and _LTI_AUTH_URL substitutions in
cloudbuild-school.yaml; on Docker they are environment variables. See
Configuration → Canvas and LTI endpoints.
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.
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:
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
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.
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.
Load the detector script
Add the detector via a Canvas theme JavaScript loader so it runs on quiz pages, and handle the self-hosted Canvas local-file (HTTP 422) case.
Verify the integration
The full acceptance sequence — administrator, instructor, student-in-browser, and student-in-SEB — to run before any real exam.