NOTE: these docs are deprecated. Please see our new Subhosting docs site here instead.
Ingress
To serve traffic with Deno Deploy Subhosting, you must send HTTP requests to the Deno Deploy Subhosting ingress endpoint. The Deno Deploy system will then find or boot an appropriate isolate to serve the request, forward the request to the isolate, and return the response after processing.
To send a request to the ingress endpoint, the request must be signed with a JWT that contains the information necessary to identify an isolate to serve the request with.
All requests to the ingress endpoint must contain a x-forwarded-host
header.
This header is used to determine the hostname to expose as the request urls’
hostname in the isolate.
Signing
All requests to the ingress endpoint must contain a x-deno-subhost
header
containing a JWT. This JWT must be signed using the shared secret provided to
you during onboarding, using the HS256 (HMAC SHA-256) algorithm.
The claims of the JWT must contain the following fields:
interface SubhostClaims {
/** Unix timestamp of the time the token was issued. */
iat: number;
/** Unix timestamp of the time until this token is valid. */
exp: number;
/** The deployment that should be used to serve the request. */
deployment_id: string;
/** The root URL of the origin RPC endpoint to use. The URL must be absolute
* and end in a slash (`/`).
*
* Example: `https://subhost-origin.example.com/v1/` */
rpc_root: string;
}
The JWT must also contain a "kid"
field in the JWT header that contains the
subhoster ID provided to you during onboarding.
Prewarm
If you know that you will be sending a request to a specific deployment, you can prewarm the deployment by sending a prewarm request (example: during TLS handshake) to the ingress endpoint. This will cause the deployment to be booted and ready to serve requests when you send the actual request and reduce the coldstart time.
The prewarm request must be signed with a JWT and sent to the ingress endpoint
with the header x-deno-prewarm
set to 1
. The JWT must contain the same
claims as the JWT used to sign the actual request.
Request Timeouts
You can control the maximum amount of time an isolate can take to process a
request by setting the x-deno-timeout-ms
header on the request. If the isolate
doesn’t respond within the specified timeout, you’ll get a 504 Gateway Timeout
status code with REQUEST_TIMED_OUT
x-deno-error header in the response.
Endpoints
The list of ingress endpoints is available in the Endpoints and Regions chapter.
If you are just getting started, a good endpoint to start with is gcp-global
:
https://subhosting-v1.deno-gcp.net
. This will automatically route requests to
the closest GCP region (by latency).
During onboarding we may provide you with alternative endpoint(s) to use that can better cater to the specific needs of your use case.
Protocol support
The ingress endpoint supports HTTP/1.1 and HTTP/2 requests. HTTP/3 may be partially supported, but should not be used. For best performance, use HTTP/2 connections.
The ingress endpoint supports full duplex streaming of requests and responses to and from the isolate.
WebSocket upgrades are supported on HTTP/1.1 connections.
Errors
It is possible for Deno Deploy Subhosting to encounter an error while processing a request. This could happen because of an error raised by user code, because the ingressed request was malformed, or because the origin RPC endpoint is unreachable. In these cases Deno Deploy will return a non-2xx response, with a body describing the error in a human-readable format.
In addition to the human readable error message, the response will also contain
an x-deno-error
header that contains a JSON object describing the error in a
machine readable way.
interface Error {
/** An error code that can be used to programmatically identify the error. */
code: string;
/** A human-readable error message. */
message: string;
}
A list of possible error codes:
Code | Description |
---|---|
INTERNAL_BOOT_RPC_ERROR |
Deno Deploy failed to make a /boot origin RPC call. |
ORIGIN_BOOT_RPC_ERROR |
The /boot origin RPC endpoint returned a failure status code. |
ORIGIN_MISSING_XDENO_CONFIG |
The /boot origin RPC endpoint did not specify a x-deno-config header. |
ORIGIN_INVALID_XDENO_CONFIG |
The /boot origin RPC endpoint specified an invalid or malformed value in the x-deno-config header. |
DEPLOYMENT_FAILED |
The isolate crashed while serving the request. Check the isolate logs. |
INVALID_HOST_HEADER |
The host /x-forwarded-host header is not valid. |
LOOP_DETECTED |
The isolate sent a request to itself. Recursive requests to the same deployment cannot be processed. |
MISSING_XFORWARDED_HOST |
The x-forwarded-host header is missing. Subhosted requests must contain a x-forwarded-host header. |
MISSING_XDENO_SUBHOST |
The x-deno-subhost header is missing. Subhosted requests must contain a x-deno-subhost header. |
INVALID_XDENO_SUBHOST |
The request contains an invalid or malformed value ein the x-deno-subhost header. |
INTERNAL_SERVER_ERROR |
An error has occurred inside of a Deno Deploy system. |
REQUEST_TIMED_OUT |
The request took longer than the maximum allowed time. |
Examples
Here is an example of sending a subhosted request to the ingress endpoint. The
origin RPC for this subhoster is https://deno-origin.example.com/v1/
.
import * as jose from "https://deno.land/x/jose@v4.8.3/index.ts";
const ORIGIN_RPC_URL = "https://deno-origin.example.com/v1/";
const SUBHOSTER_ID = "example";
const SHARED_SECRET = new TextEncoder().encode(Deno.env.get("SHARED_SECRET"));
// Create the JWT token to send along with the request.
const iat = Math.floor(Date.now() / 1000); // Unix timestamp of the time the token was issued.
const exp = iat + 15 * 60; // Unix timestamp of the time until this token is valid. (t + 15min)
const claims = {
iat,
exp,
deployment_id: "example",
rpc_root: ORIGIN_RPC_URL,
};
const jwt = await new jose.SignJWT(claims)
.setProtectedHeader({ typ: "JWT", alg: "HS256", kid: SUBHOSTER_ID })
.sign(SHARED_SECRET);
// Send the request to the ingress endpoint.
const resp = await fetch("https://v1.subhosting.deno.dev/", {
method: "GET",
headers: {
"x-deno-subhost": jwt,
"x-forwarded-host": "example.com",
},
});
console.log("Response status:", resp.status);
console.log("Response body:", await resp.text());