{"id":44948,"date":"2025-11-11T10:32:15","date_gmt":"2025-11-11T15:32:15","guid":{"rendered":"https:\/\/netfoundry.io\/?p=44948"},"modified":"2025-11-11T13:15:09","modified_gmt":"2025-11-11T18:15:09","slug":"how-to-secure-spa-api-calls-without-exposing-your-backend","status":"publish","type":"post","link":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/","title":{"rendered":"How to Secure SPA API Calls Without Exposing Your Backend"},"content":{"rendered":"\n<h3 class=\"wp-block-heading has-text-align-left\" id=\"h-introducing-the-openziti-browser-sdk-embedded-zero-trust-for-web-apps\"><strong><strong>Introducing the OpenZiti Browser SDK \u2013 embedded zero-trust for web apps<\/strong><\/strong><\/h3>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-problem\">The Problem<\/h2>\n\n\n\n<p>Modern single-page applications (<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Glossary\/SPA\">SPAs<\/a>) run entirely in the browser. Every click, every update, every dashboard refresh \u2014 it all happens through JavaScript, making API calls directly to backend services.<\/p>\n\n\n\n<p>That architecture is fast and elegant. But there\u2019s a catch: SPAs can only talk to APIs that are reachable from the public internet. Once you expose those APIs, you inherit every modern attack vector \u2014 scanning, credential stuffing, API key theft, and more.<\/p>\n\n\n\n<p>Traditional defenses like WAFs, IP whitelists, or OAuth2 help, but they don\u2019t eliminate exposure. You still have something listening on the open internet.<\/p>\n\n\n\n<p>In short: SPAs are easy to build, but hard to secure \u2014 unless you can make their backend APIs completely invisible.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-traditional-options-and-their-gaps\"><strong>The Traditional Options (and Their Gaps)<\/strong><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Approach<\/strong><\/td><td><strong>Pros<\/strong><\/td><td><strong>Cons<\/strong><\/td><\/tr><tr><td>WAF \/ API Gateway<\/td><td>Easy to integrate<\/td><td>Still exposes endpoints publicly<\/td><\/tr><tr><td>VPN \/ Tunneler<\/td><td>Keeps APIs private<\/td><td>Requires client install; poor UX<\/td><\/tr><tr><td>Reverse Proxy<\/td><td>Simplifies routing<\/td><td>Adds attack surface<\/td><\/tr><tr><td>Zero-Trust Broker<\/td><td>Policy-based access<\/td><td>Doesn\u2019t embed directly in app; often opaque or costly<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>SPA Developers and operations teams need something that:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Keeps APIs off the internet<\/li>\n\n\n\n<li>Doesn\u2019t require installing a tunneler<\/li>\n\n\n\n<li>Works natively from the browser<\/li>\n\n\n\n<li>Supports modern identity and policy models<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-netfoundry-approach\">The NetFoundry Approach<\/h2>\n\n\n\n<p><strong><a href=\"https:\/\/netfoundry.io\/docs\/openziti\/learn\/introduction\">OpenZiti<\/a><\/strong> is an open-source, zero-trust overlay network technology, developed by NetFoundry, that embeds secure connectivity directly into applications. Instead of building a perimeter around your network, you bring zero-trust into the app itself.<\/p>\n\n\n\n<p>Connections are mutually authenticated, encrypted, and policy-controlled. No open inbound ports. No VPNs. No public exposure.<\/p>\n\n\n\n<p>For those unfamiliar, <strong>NetFoundry<\/strong> provides a cloud-managed service built on OpenZiti \u2014 adding:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Hosted, dedicated, private overlays<\/li>\n\n\n\n<li>Automated provisioning and lifecycle management<\/li>\n\n\n\n<li>Deep telemetry and observability<\/li>\n\n\n\n<li>Compliance options (FIPS, HIPAA, NIST, PCI, NERC CIP)<\/li>\n\n\n\n<li>Hybrid\/air-gapped deployment flexibility<\/li>\n\n\n\n<li>Enterprise performance, integrations, features, SLAs, and support<\/li>\n<\/ul>\n\n\n\n<p>And now, a way to extend all of that directly into the browser.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-introducing-the-openziti-browser-sdk\"><strong>Introducing the OpenZiti Browser SDK<\/strong><\/h2>\n\n\n\n<p>As we modernized our NetFoundry cloud-managed service from a multi-page app (MPA) to a single-page app (SPA), we faced a challenge: how to let the SPA securely call protected management APIs without exposing them to the internet.<\/p>\n\n\n\n<p>Running a local tunneler would have worked \u2014 but that\u2019s friction for users.<\/p>\n\n\n\n<p>So we built and open-sourced the <a href=\"https:\/\/github.com\/openziti\/ziti-sdk-browser\">@openziti\/ziti-sdk-browser<\/a>, an SDK that brings OpenZiti\u2019s zero-trust connectivity directly into web apps.<\/p>\n\n\n\n<p>This SDK:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Authenticates with the OpenZiti Controller<\/li>\n\n\n\n<li>Negotiates an ephemeral x509 certificate<\/li>\n\n\n\n<li>Establishes a mutual TLS (mTLS) connection<\/li>\n\n\n\n<li>Routes HTTPS requests securely over the OpenZiti network<\/li>\n<\/ul>\n\n\n\n<p>All without exposing any backend to the public internet or requiring extra software.&nbsp;<\/p>\n\n\n\n<p>In essence, your browser becomes a zero-trust endpoint.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-how-it-works-in-netfoundry-cloud\"><strong>How it Works in NetFoundry Cloud<\/strong><\/h2>\n\n\n\n<p>Typically, the <a href=\"https:\/\/netfoundry.io\/docs\/openziti\/reference\/developer\/api\/edge-client-reference\">OpenZiti Edge API<\/a> is Internet-facing, while the <a href=\"https:\/\/netfoundry.io\/docs\/openziti\/reference\/developer\/api\/edge-management-reference\">OpenZiti Management API<\/a> is protected. Since OpenZiti is purpose-built for secure connectivity, we secure the management API within NetFoundry Cloud by using OpenZiti itself, making the management API invisible to the open internet.<\/p>\n\n\n\n<p>The OpenZiti Controller defines the <strong>edge API<\/strong>, and it is intended for use by endpoints\/clients to authenticate, discover services, dial (connect), or bind (host) services over the Ziti overlay.<\/p>\n\n\n\n<p>The <strong>management API<\/strong> is also exposed by the OpenZiti Controller, but is used to <strong><em>manage<\/em><\/strong> the network \u2013 configuring identities, services, policies, etc. It is intended for administrative control-plane tasks (creating, updating, and deleting entities) rather than actually connecting services.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><\/td><td><strong>Management API<\/strong><\/td><td><strong>Edge API<\/strong><\/td><\/tr><tr><td><strong>Purpose<\/strong><\/td><td>Configuration\/administration of the Ziti overlay (identities, services, roles\/policies)<\/td><td>Runtime connectivity: manage an endpoint\u2019s participation (auth, discovery, connect\/bind)<\/td><\/tr><tr><td><strong>Typical users<\/strong><\/td><td>Human Admins and automated orchestration tooling<\/td><td>Endpoint apps, SDKs embedded in apps, and client devices<\/td><\/tr><tr><td><strong>Permissions\/usage<\/strong><\/td><td>Needs elevated privileges (create identity, service, policy)<\/td><td>Less privileged relative to the overlay (once identity is enrolled)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>As you can see, the <strong>management API<\/strong> is quite powerful. If it were to become compromised, bad actors could do catastrophic damage to an OpenZiti network.<\/p>\n\n\n\n<p>API security is a critical part of modern web, mobile, and cloud architecture. Some industry-standard mechanisms used to secure APIs today include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>API Keys<\/li>\n\n\n\n<li>OAuth2\/OIDC<\/li>\n\n\n\n<li>Mutual TLS (mTLS)<\/li>\n\n\n\n<li>JWTs<\/li>\n\n\n\n<li>WAFs<\/li>\n\n\n\n<li>IP Whitelists\/CIDR restriction<\/li>\n\n\n\n<li>Security Headers in HTTP requests<\/li>\n\n\n\n<li>And of course, Zero Trust<\/li>\n<\/ul>\n\n\n\n<p>The NetFoundry Cloud\u2019s <strong>management API<\/strong> does not suffer from the typical API attack vector exposures. In addition to the robust authentication-security that OpenZiti employs, an additional and very effective way to eliminate API attack vectors is to make the API <strong><em>invisible<\/em><\/strong> to the open internet.&nbsp;<\/p>\n\n\n\n<p>Let\u2019s explore what that means and how NetFoundry Cloud achieves \u201c<em>invisibility<\/em>\u201d for the <strong>management API<\/strong>, thus boosting the defensive posture of all NetFoundry networks.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-achieving-invisibility\"><strong>Achieving Invisibility<\/strong><\/h2>\n\n\n\n<p>OpenZiti supports the innovative approach of embedding secure zero-trust connectivity directly into applications (even browser-based web apps).<\/p>\n\n\n\n<p>The NetFoundry Cloud SPA now embeds and integrates with our newly open-sourced OpenZiti SDK for browser-based web apps (<a href=\"https:\/\/github.com\/openziti\/ziti-sdk-browser\"><strong>@openziti\/ziti-sdk-browser<\/strong><\/a>).<\/p>\n\n\n\n<p>Again, within the NetFoundry Cloud SPA, the <strong>ziti-sdk-browser<\/strong> takes care of all operations related to:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>authenticating with the OpenZiti Controller<\/li>\n\n\n\n<li>doing the necessary negotiations with the OpenZiti Controller to acquire an ephemeral x509 certificate<\/li>\n\n\n\n<li>using this certificate to make the required mTLS connection to the network<\/li>\n\n\n\n<li>mapping any HTTPS Requests initiated by the NetFoundry Cloud console that target the <strong>management API<\/strong> onto the OpenZiti network and then<\/li>\n\n\n\n<li>routing them to the&nbsp; <strong>nf-mgmt-service <\/strong>described below.<\/li>\n<\/ul>\n\n\n\n<p>Admin users simply open a Chrome Tab on any computer, log in to the NetFoundry Cloud console, and they\u2019re ready to go!<\/p>\n\n\n\n<p>Before the availability of our new browser SDK, the most common way to facilitate client access to remote OpenZiti Services was to:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Create an Identity, then&nbsp;<\/li>\n\n\n\n<li>Install an <a href=\"https:\/\/netfoundry.io\/docs\/openziti\/reference\/tunnelers\/\"><strong>OpenZiti Tunneler<\/strong><\/a> on the client machine, then&nbsp;<\/li>\n\n\n\n<li>Enroll the Identity within the tunneler, then&nbsp;<\/li>\n\n\n\n<li>Configure the various role attributes for the Identity, then&nbsp;<\/li>\n\n\n\n<li>Connect the tunneler to the network, then<\/li>\n\n\n\n<li>Finally, open a Chrome browser tab and go to the NetFoundry Cloud web console, and have access to any UI gestures that leverage the management API&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>As you can see, by leveraging the <strong>ziti-sdk-browser<\/strong>, NetFoundry Cloud now has a much more streamlined approach.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-representing-the-management-api-in-a-netfoundry-cloud-network\"><strong>Representing the Management API in a NetFoundry Cloud Network<\/strong><\/h2>\n\n\n\n<p>Let\u2019s begin by discussing how the <strong>management API<\/strong> is manifested in the networks provisioned by NetFoundry Cloud (<em>i.e., this does not happen by default in a self-hosted OpenZiti network<\/em>).<\/p>\n\n\n\n<p>When NetFoundry Cloud provisions your network, it automatically performs the tasks necessary to represent the <strong>management API<\/strong> as an <a href=\"https:\/\/netfoundry.io\/docs\/openziti\/learn\/introduction\/components#services\"><strong>OpenZiti Service<\/strong><\/a>.&nbsp;<\/p>\n\n\n\n<p>NetFoundry Cloud names the Service the <strong>nf-mgmt-service,<\/strong> as shown here:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"638\" height=\"406\" src=\"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-1.png\" alt=\"\" class=\"wp-image-44950\" srcset=\"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-1.png 638w, https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-1-300x191.png 300w\" sizes=\"auto, (max-width: 638px) 100vw, 638px\" \/><\/figure>\n\n\n\n<p>Like all Services, steps are taken during network cloud-provisioning to ensure the <strong>management API <\/strong>does not listen on any open inbound ports on the underlying IP network.&nbsp;<\/p>\n\n\n\n<p>Furthermore, NetFoundry Cloud also ensures that client access to the <strong>nf-mgmt-service<\/strong> is granted only after strict authentication and authorization is achieved \u2013 based on strong, cryptographically verifiable <a href=\"https:\/\/netfoundry.io\/docs\/openziti\/learn\/introduction\/components#identities\"><strong>Identities<\/strong><\/a>.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-micro-segmentation\"><strong>Micro-segmentation<\/strong><\/h2>\n\n\n\n<p>Another fundamental feature delivered by the NetFoundry Cloud is <a href=\"https:\/\/en.wikipedia.org\/wiki\/Microsegmentation_(network_security)\">micro-segmentation<\/a>.<\/p>\n\n\n\n<p>Having a valid Identity on a NetFoundry network (i.e., completion of successful <strong><em>authentication<\/em><\/strong>) does not necessarily mean access to the <strong>nf-mgmt-service<\/strong> is possible for the Identity.<\/p>\n\n\n\n<p>Access to the <strong>nf-mgmt-service<\/strong> is controlled by <a href=\"https:\/\/netfoundry.io\/docs\/openziti\/learn\/introduction\/components#policies\"><strong>Roles and Policies<\/strong><\/a> assigned to the <strong>nf-mgmt-service, <\/strong>and which are mapped onto any Identities that require access due to their administrative needs (i.e., <strong><em>authorization<\/em><\/strong> is necessary).<\/p>\n\n\n\n<p>If an Identity is justified in needing access to the <strong>nf-mgmt-service, <\/strong>that Identity will be granted Dial (connect) access by virtue of being assigned the <strong>nf-mgmt-access <\/strong><a href=\"https:\/\/netfoundry.io\/docs\/openziti\/learn\/introduction\/components#role-attributes\"><strong>Attribute<\/strong><\/a>, as shown here:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"990\" height=\"496\" src=\"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-2.png\" alt=\"\" class=\"wp-image-44951\" srcset=\"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-2.png 990w, https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-2-300x150.png 300w, https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-2-768x385.png 768w\" sizes=\"auto, (max-width: 990px) 100vw, 990px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"948\" height=\"402\" src=\"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-3.png\" alt=\"\" class=\"wp-image-44952\" srcset=\"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-3.png 948w, https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-3-300x127.png 300w, https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-3-768x326.png 768w\" sizes=\"auto, (max-width: 948px) 100vw, 948px\" \/><\/figure>\n\n\n\n<p>Above, you can see that the Identity used in this example is \u201cmanaged\u201d. A \u201cmanaged\u201d Identity is one that NetFoundry Cloud automatically provisioned on behalf of the privileged admin user (me in this case) as derived from OIDC-based credentials, which are used to log in to the NetFoundry Cloud console.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-developer-experience\"><strong><strong>Developer Experience<\/strong><\/strong><\/h2>\n\n\n\n<p>The <strong>ziti-sdk-browser<\/strong> is designed to simplify routing HTTP Requests originating from the web app onto a protected OpenZiti Service.&nbsp;<\/p>\n\n\n\n<p>The SDK exposes a fetch API that mimics the native browser <strong><code>fetch<\/code><\/strong> API, so the structure and flow should feel familiar.<\/p>\n\n\n\n<p>For example, once the target resource (API Server) has been properly configured as an OpenZiti Service (similar to what was described in the previous section), HTTP Requests can be easily routed to the Service as shown in the following TypeScript code.<\/p>\n\n\n\n<p>Here\u2019s what it looks like in practice:<\/p>\n\n\n\n<p><em>Instantiate a zitiBrowserClient (one-time operation during app initialization)<\/em>:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"447\" src=\"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-5-1024x447.png\" alt=\"\" class=\"wp-image-44956\" srcset=\"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-5-1024x447.png 1024w, https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-5-300x131.png 300w, https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-5-768x335.png 768w, https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-5.png 1374w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><em>Make a fetch request (this code shows how to map Angular <code><strong>HttpRequest\u2019s<\/strong><\/code> onto OpenZiti):&nbsp;<\/em><br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"889\" src=\"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-6-1024x889.png\" alt=\"\" class=\"wp-image-44957\" srcset=\"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-6-1024x889.png 1024w, https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-6-300x261.png 300w, https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-6-768x667.png 768w, https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image-6.png 1370w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>The same API URLs and code patterns continue to work \u2014 the SDK simply intercepts and routes them through the OpenZiti network.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-why-this-matters\">Why This Matters<\/h2>\n\n\n\n<p>By embedding the OpenZiti Browser SDK into our SPA, NetFoundry Cloud now offers:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>True zero exposure: no public-facing APIs<\/li>\n\n\n\n<li>Frictionless user experience: no tunneler installation<\/li>\n\n\n\n<li>Granular access control: micro-segmented, role-based authorization<\/li>\n\n\n\n<li>Strong cryptographic identity: every connection is authenticated<\/li>\n\n\n\n<li>Drop-in simplicity: works like native fetch()<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-who-this-is-for\"><strong>Who This Is For<\/strong><\/h2>\n\n\n\n<p>Use the OpenZiti Browser SDK if you:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Build SPAs or web consoles that call backend APIs<\/li>\n\n\n\n<li>Need those APIs to remain invisible to the internet<\/li>\n\n\n\n<li>Want zero-trust connectivity without installing tunnelers<\/li>\n\n\n\n<li>Already use or plan to use OpenZiti or NetFoundry Cloud<\/li>\n<\/ul>\n\n\n\n<p>If that\u2019s you, your browser can now be a first-class, zero-trust endpoint.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion-come-talk-to-us\"><strong>Conclusion<\/strong> (Come Talk to Us)<\/h2>\n\n\n\n<p>Zero trust doesn\u2019t have to live in a gateway or VPN client. It can live directly inside your application \u2014 even your browser.<\/p>\n\n\n\n<p>While a deeper discussion of the internals of the <strong>ziti-sdk-browser<\/strong> (e.g., how we use WASM for PKI and nested TLS operations) is beyond the scope of this article, we hope this high-level discussion demonstrates how NetFoundry\u2019s state-of-the-art technology is constantly improving ways to make your networks not only more secure but also more convenient to use. <\/p>\n\n\n\n<p><strong>If this sounds interesting to you, <a href=\"https:\/\/netfoundry.io\/lets-talk\/\">reach out and talk to us<\/a> about getting access to NetFoundry Cloud.<\/strong><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introducing the OpenZiti Browser SDK \u2013 embedded zero-trust for web apps.<\/p>\n","protected":false},"author":116,"featured_media":44949,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"content-type":"","footnotes":""},"categories":[860],"tags":[856,528,979,545],"class_list":["post-44948","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-embeddable-zero-trust","tag-embedded-security","tag-openziti","tag-spa","tag-zero-trust"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.3 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>How to Secure SPA API Calls Without Exposing Your Backend - NetFoundry<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Secure SPA API Calls Without Exposing Your Backend\" \/>\n<meta property=\"og:description\" content=\"Introducing the OpenZiti Browser SDK \u2013 embedded zero-trust for web apps.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/\" \/>\n<meta property=\"og:site_name\" content=\"NetFoundry\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/netfoundry.io\" \/>\n<meta property=\"article:published_time\" content=\"2025-11-11T15:32:15+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-11T18:15:09+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image.png\" \/>\n\t<meta property=\"og:image:width\" content=\"612\" \/>\n\t<meta property=\"og:image:height\" content=\"408\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Curt Tudor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@netfoundry\" \/>\n<meta name=\"twitter:site\" content=\"@netfoundry\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Curt Tudor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/\"},\"author\":{\"name\":\"Curt Tudor\",\"@id\":\"https:\\\/\\\/netfoundry.io\\\/#\\\/schema\\\/person\\\/3ffae2075a283a90dc02d2ee3f336b51\"},\"headline\":\"How to Secure SPA API Calls Without Exposing Your Backend\",\"datePublished\":\"2025-11-11T15:32:15+00:00\",\"dateModified\":\"2025-11-11T18:15:09+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/\"},\"wordCount\":1638,\"publisher\":{\"@id\":\"https:\\\/\\\/netfoundry.io\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/netfoundry.io\\\/wp-content\\\/uploads\\\/2025\\\/11\\\/image.png\",\"keywords\":[\"Embedded Security\",\"OpenZiti\",\"SPA\",\"Zero Trust\"],\"articleSection\":[\"Embeddable Zero Trust\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/\",\"url\":\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/\",\"name\":\"How to Secure SPA API Calls Without Exposing Your Backend - NetFoundry\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/netfoundry.io\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/netfoundry.io\\\/wp-content\\\/uploads\\\/2025\\\/11\\\/image.png\",\"datePublished\":\"2025-11-11T15:32:15+00:00\",\"dateModified\":\"2025-11-11T18:15:09+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/#primaryimage\",\"url\":\"https:\\\/\\\/netfoundry.io\\\/wp-content\\\/uploads\\\/2025\\\/11\\\/image.png\",\"contentUrl\":\"https:\\\/\\\/netfoundry.io\\\/wp-content\\\/uploads\\\/2025\\\/11\\\/image.png\",\"width\":612,\"height\":408,\"caption\":\"Introducing the OpenZiti Browser SDK \u2013 embedded zero-trust for web apps\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/netfoundry.io\\\/embeddable-zero-trust\\\/how-to-secure-spa-api-calls-without-exposing-your-backend\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/netfoundry.io\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Secure SPA API Calls Without Exposing Your Backend\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/netfoundry.io\\\/#website\",\"url\":\"https:\\\/\\\/netfoundry.io\\\/\",\"name\":\"NetFoundry\",\"description\":\"Identity-First\u2122 Networking\",\"publisher\":{\"@id\":\"https:\\\/\\\/netfoundry.io\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/netfoundry.io\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/netfoundry.io\\\/#organization\",\"name\":\"NetFoundry\",\"url\":\"https:\\\/\\\/netfoundry.io\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/netfoundry.io\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/netfoundry.io\\\/wp-content\\\/uploads\\\/2024\\\/08\\\/netfoundry-icon-color.png\",\"contentUrl\":\"https:\\\/\\\/netfoundry.io\\\/wp-content\\\/uploads\\\/2024\\\/08\\\/netfoundry-icon-color.png\",\"width\":512,\"height\":512,\"caption\":\"NetFoundry\"},\"image\":{\"@id\":\"https:\\\/\\\/netfoundry.io\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/netfoundry.io\",\"https:\\\/\\\/x.com\\\/netfoundry\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/netfoundry\\\/\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UCGN6PFj1rZu50yme9YsICmg\",\"https:\\\/\\\/www.instagram.com\\\/netfoundry.io\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/netfoundry.io\\\/#\\\/schema\\\/person\\\/3ffae2075a283a90dc02d2ee3f336b51\",\"name\":\"Curt Tudor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/18ee19a7ad900fbeac2921a2299c0a0458af7296c4d43eb3705639d18e9c9cb9?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/18ee19a7ad900fbeac2921a2299c0a0458af7296c4d43eb3705639d18e9c9cb9?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/18ee19a7ad900fbeac2921a2299c0a0458af7296c4d43eb3705639d18e9c9cb9?s=96&d=mm&r=g\",\"caption\":\"Curt Tudor\"},\"url\":\"https:\\\/\\\/netfoundry.io\\\/author\\\/curt-tudor\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"How to Secure SPA API Calls Without Exposing Your Backend - NetFoundry","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/","og_locale":"en_US","og_type":"article","og_title":"How to Secure SPA API Calls Without Exposing Your Backend","og_description":"Introducing the OpenZiti Browser SDK \u2013 embedded zero-trust for web apps.","og_url":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/","og_site_name":"NetFoundry","article_publisher":"https:\/\/www.facebook.com\/netfoundry.io","article_published_time":"2025-11-11T15:32:15+00:00","article_modified_time":"2025-11-11T18:15:09+00:00","og_image":[{"width":612,"height":408,"url":"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image.png","type":"image\/png"}],"author":"Curt Tudor","twitter_card":"summary_large_image","twitter_creator":"@netfoundry","twitter_site":"@netfoundry","twitter_misc":{"Written by":"Curt Tudor","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/#article","isPartOf":{"@id":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/"},"author":{"name":"Curt Tudor","@id":"https:\/\/netfoundry.io\/#\/schema\/person\/3ffae2075a283a90dc02d2ee3f336b51"},"headline":"How to Secure SPA API Calls Without Exposing Your Backend","datePublished":"2025-11-11T15:32:15+00:00","dateModified":"2025-11-11T18:15:09+00:00","mainEntityOfPage":{"@id":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/"},"wordCount":1638,"publisher":{"@id":"https:\/\/netfoundry.io\/#organization"},"image":{"@id":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/#primaryimage"},"thumbnailUrl":"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image.png","keywords":["Embedded Security","OpenZiti","SPA","Zero Trust"],"articleSection":["Embeddable Zero Trust"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/","url":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/","name":"How to Secure SPA API Calls Without Exposing Your Backend - NetFoundry","isPartOf":{"@id":"https:\/\/netfoundry.io\/#website"},"primaryImageOfPage":{"@id":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/#primaryimage"},"image":{"@id":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/#primaryimage"},"thumbnailUrl":"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image.png","datePublished":"2025-11-11T15:32:15+00:00","dateModified":"2025-11-11T18:15:09+00:00","breadcrumb":{"@id":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/#primaryimage","url":"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image.png","contentUrl":"https:\/\/netfoundry.io\/wp-content\/uploads\/2025\/11\/image.png","width":612,"height":408,"caption":"Introducing the OpenZiti Browser SDK \u2013 embedded zero-trust for web apps"},{"@type":"BreadcrumbList","@id":"https:\/\/netfoundry.io\/embeddable-zero-trust\/how-to-secure-spa-api-calls-without-exposing-your-backend\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/netfoundry.io\/"},{"@type":"ListItem","position":2,"name":"How to Secure SPA API Calls Without Exposing Your Backend"}]},{"@type":"WebSite","@id":"https:\/\/netfoundry.io\/#website","url":"https:\/\/netfoundry.io\/","name":"NetFoundry","description":"Identity-First\u2122 Networking","publisher":{"@id":"https:\/\/netfoundry.io\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/netfoundry.io\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/netfoundry.io\/#organization","name":"NetFoundry","url":"https:\/\/netfoundry.io\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/netfoundry.io\/#\/schema\/logo\/image\/","url":"https:\/\/netfoundry.io\/wp-content\/uploads\/2024\/08\/netfoundry-icon-color.png","contentUrl":"https:\/\/netfoundry.io\/wp-content\/uploads\/2024\/08\/netfoundry-icon-color.png","width":512,"height":512,"caption":"NetFoundry"},"image":{"@id":"https:\/\/netfoundry.io\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/netfoundry.io","https:\/\/x.com\/netfoundry","https:\/\/www.linkedin.com\/company\/netfoundry\/","https:\/\/www.youtube.com\/channel\/UCGN6PFj1rZu50yme9YsICmg","https:\/\/www.instagram.com\/netfoundry.io"]},{"@type":"Person","@id":"https:\/\/netfoundry.io\/#\/schema\/person\/3ffae2075a283a90dc02d2ee3f336b51","name":"Curt Tudor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/18ee19a7ad900fbeac2921a2299c0a0458af7296c4d43eb3705639d18e9c9cb9?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/18ee19a7ad900fbeac2921a2299c0a0458af7296c4d43eb3705639d18e9c9cb9?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/18ee19a7ad900fbeac2921a2299c0a0458af7296c4d43eb3705639d18e9c9cb9?s=96&d=mm&r=g","caption":"Curt Tudor"},"url":"https:\/\/netfoundry.io\/author\/curt-tudor\/"}]}},"_links":{"self":[{"href":"https:\/\/netfoundry.io\/wp-json\/wp\/v2\/posts\/44948","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/netfoundry.io\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/netfoundry.io\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/netfoundry.io\/wp-json\/wp\/v2\/users\/116"}],"replies":[{"embeddable":true,"href":"https:\/\/netfoundry.io\/wp-json\/wp\/v2\/comments?post=44948"}],"version-history":[{"count":0,"href":"https:\/\/netfoundry.io\/wp-json\/wp\/v2\/posts\/44948\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/netfoundry.io\/wp-json\/wp\/v2\/media\/44949"}],"wp:attachment":[{"href":"https:\/\/netfoundry.io\/wp-json\/wp\/v2\/media?parent=44948"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/netfoundry.io\/wp-json\/wp\/v2\/categories?post=44948"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/netfoundry.io\/wp-json\/wp\/v2\/tags?post=44948"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}