Skip to main content

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());