ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference
Introduction
Getting Started
    Develop in the portal
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
    Develop locally with the CLI
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
Concepts
Development
Policies
Handlers
API Keys
Rate Limiting
Caching
MCP Server
MCP Gateway
AI Gateway
Developer Portal
Monetization
GraphQL
Deploying & Source Control
Analytics
Observability
Networking & Infrastructure
    Overview
    Managed Dedicated
    Managed EdgeSelf Hosted
    Custom Domains
    Securing Your Backend
      Securing your backend
      IAM Authentication
      mTLS
      Secure Tunnel
        OverviewSetupConnect to ServicesAdvancedTroubleshooting
    Web Application Firewalls
    DDoS Protection
Account Management
Programming API
Build with AI
Zuplo CLI
Migration Guides
Platform LimitsVersion Support PolicySecuritySupportTrust & ComplianceChangelog
powered by Zudoku
Secure Tunnel

Connect to Tunnel Services

Enterprise Feature

Secure tunneling is available as an add-on as part of an enterprise plan. If you would like to purchase this feature, please contact us at sales@zuplo.com or reach out to your account manager.

Most enterprise features can be used in a trial mode for a limited time. Feel free to use enterprise features for development and testing purposes.

Once a tunnel exposes a service, your gateway calls it like any other backend — with a URL. Tunnel services use the service:// scheme instead of a hostname, so a service named payments-api is reachable at service://payments-api.

This page covers the service configuration file, and the three places you use a service:// URL: handler code, route configuration, and environment variables. To create a tunnel first, see Set up a tunnel.

Service configuration reference

You define services in a JSON file and upload it with zuplo tunnel services update. Each upload replaces the tunnel's entire configuration.

Code
{ "version": 1, "services": [ { "name": "payments-api-prod", "endpoint": "http://payments.internal:8080", "configurations": [ { "project": "my-project", "accessibleBy": ["production"] }, { "project": "my-other-project", "accessibleBy": ["production"] } ] }, { "name": "payments-api-staging", "endpoint": "http://payments-staging.internal:8080", "configurations": [ { "project": "my-project", "accessibleBy": ["preview", "working-copy"] } ] } ] }
PropertyTypeDescription
versionnumberConfiguration format version. Use 1.
servicesarrayThe services this tunnel exposes.
services[].namestringThe service name your gateway calls, used as service://<name>.
services[].endpointstringThe internal URL the tunnel forwards to, resolvable from the tunnel host. For example, http://payments.internal:8080.
services[].configurationsarrayWhich projects and environments can call this service. A project that isn't listed can't reach the service.
configurations[].projectstringThe name of the Zuplo project.
configurations[].accessibleBystring[]The environments allowed to call the service. Valid values are production, preview, and working-copy.

Use accessibleBy to keep environments apart. A service listed only under production can't be reached from a preview build, even by a developer on your own team.

To see the configuration a tunnel currently has, run zuplo tunnel services describe.

Call a service from code

Pass the service:// URL to fetch exactly as you would a public URL:

Code
import { ZuploContext, ZuploRequest } from "@zuplo/runtime"; export default async function (request: ZuploRequest, context: ZuploContext) { const response = await fetch("service://payments-api-prod/v1/charges"); if (!response.ok) { context.log.error(`Payments API returned ${response.status}`); return new Response("Upstream error", { status: 502 }); } return response; }

Paths, query strings, headers, and request methods all work the way they do for a normal fetch. The tunnel appends the path to the service's configured endpoint.

Call a service from route configuration

Any route property that accepts a URL accepts a service:// URL, including the URL Rewrite handler:

Code
{ "paths": { "/v1/charges": { "get": { "x-zuplo-route": { "handler": { "export": "urlRewriteHandler", "module": "$import(@zuplo/runtime)", "options": { "rewritePattern": "service://payments-api-prod/v1/charges" } } } } } } }

You can also set the rewrite URL in the Rewrite URL field of the route editor in the Zuplo Portal.

Switch services per environment

Most teams run a separate internal service for each environment. Rather than changing code or route configuration per environment, store the service URL in an environment variable and set a different value in each Zuplo environment.

In your production environment, point the variable at the production service:

Code
TUNNEL_BASE_URL=service://payments-api-prod

In preview and working-copy environments, point it at staging:

Code
TUNNEL_BASE_URL=service://payments-api-staging

Read the variable in handler code:

Code
import { ZuploContext, ZuploRequest, environment } from "@zuplo/runtime"; export default async function (request: ZuploRequest, context: ZuploContext) { return fetch(`${environment.TUNNEL_BASE_URL}/v1/charges`); }

Environment variables work in route configuration too. The following URL Rewrite handler combines the variable with a path parameter:

The Request Handler pane of the Zuplo route editor, with Handler set to URL Rewrite and Rewrite URL set to a value that combines the TUNNEL_BASE_URL environment variable with a path parameter

Next steps

  • Advanced tunnel configuration — the cloudflared foundation, environment variables, and self-managed images.
  • Troubleshoot a tunnel — diagnose requests that don't reach your backend.
Edit this page
Last modified on August 5, 2026
SetupAdvanced
On this page
  • Service configuration reference
  • Call a service from code
  • Call a service from route configuration
  • Switch services per environment
  • Next steps
JSON
TypeScript
JSON
TypeScript