Backend & CORS
The widget renders entirely in the browser, but it calls Hobba’s backend for
data and to submit transactions. By default these go to https://app.hobba.io,
which you can override with the apiBaseUrl option.
The origin allowlist
Section titled “The origin allowlist”In production, Hobba’s backend gates the sensitive routes by Origin: it only
returns CORS headers for origins on its allowlist. A cross-origin request from
an origin that isn’t allowlisted fails its CORS preflight - the browser blocks
it because no Access-Control-Allow-Origin header comes back.
The backend does not reject these requests server-side - it simply omits the CORS header, so the browser is what blocks the cross-origin call.
This means a default embed (calling app.hobba.io directly from your origin)
works for read-only data but the transacting routes will be CORS-blocked
until your origin is handled. You have two ways to handle it.
Option 1 - get your origin allowlisted
Section titled “Option 1 - get your origin allowlisted”Ask the Hobba team to add your deployed origin (e.g. https://yourapp.com) to
the allowlist. Once it’s on the list, calls from your site to
app.hobba.io get the CORS headers they need and everything works with the
default apiBaseUrl. This is the simplest path for a production embed on a
fixed domain.
Option 2 - proxy /api through your own origin
Section titled “Option 2 - proxy /api through your own origin”Point apiBaseUrl at your own origin (or a backend you control) and forward
/api/* to app.hobba.io server-side. Because the browser then only ever talks
to your own origin, there’s no cross-origin request and no CORS preflight at all
- so no allowlist change is needed.
When you proxy, present the forwarded request as a first-party call (set the
Origin / Referer headers to https://app.hobba.io) so the backend treats it
normally.
Local development
Section titled “Local development”The same allowlist makes plain localhost development awkward: localhost is
not allowlisted, so direct calls to app.hobba.io get CORS-blocked.
The clean fix is a dev proxy - route /api/* same-origin through your dev
server, which forwards to app.hobba.io server-side. With Vite:
export default defineConfig({ server: { proxy: { "/api": { target: "https://app.hobba.io", changeOrigin: true, secure: true, configure: (proxy) => { // Present as a first-party call so the backend treats it normally. proxy.on("proxyReq", (proxyReq) => { proxyReq.setHeader("origin", "https://app.hobba.io"); proxyReq.setHeader("referer", "https://app.hobba.io/"); }); }, }, }, },});Then point the widget’s apiBaseUrl at your dev origin so its calls go through
the proxy. The widget builds a web3.js Connection from an RPC endpoint under
apiBaseUrl, which needs an absolute URL - use window.location.origin, not
an empty string:
const apiBaseUrl = import.meta.env.DEV ? window.location.origin // → dev proxy, same-origin, no CORS : "https://app.hobba.io"; // → production default (must be allowlisted)The browser only ever talks to localhost, so both the read routes and the
transacting routes work in local dev with no allowlist change.
Summary
Section titled “Summary”| Scenario | apiBaseUrl | What’s required |
|---|---|---|
| Read-only data, any origin | default (app.hobba.io) | Nothing |
| Production embed, transacting | default (app.hobba.io) | Your origin on the allowlist |
| Production embed, self-hosted | your origin | Proxy /api/* → app.hobba.io |
| Local dev | window.location.origin | Dev proxy → app.hobba.io |