> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usedatabrain.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Proxy Authentication

> Configure a proxy server to securely forward requests to your Databrain backend with authentication.

<Info>
  This page explains how to:

  1. Create YOUR OWN proxy server that forwards requests to your Databrain backend
  2. Configure the proxy to validate requests and add authentication headers

  Your proxy acts as a secure gateway between your frontend and the Databrain backend.
</Info>

Proxy authentication lets you host a secure intermediary server that validates incoming requests and forwards them to your Databrain backend. This approach keeps your plugin tokens secure on your server, never exposing them to the client.

<Note>
  **Enhanced Security:** With proxy authentication, your frontend never handles plugin tokens. Your proxy validates requests and adds the required authentication before forwarding to Databrain.
</Note>

## How Proxy Authentication Works

```mermaid theme={"dark"}
sequenceDiagram
    participant Client as Client (Browser)
    participant Proxy as Your Proxy Server
    participant Backend as Databrain Backend

    Client->>Proxy: Request with X-Proxy-Auth-Key (or X-Authorization)
    Proxy->>Proxy: Validate & add X-Plugin-Token
    Proxy->>Backend: Forward request (method + body + headers)
    Backend-->>Proxy: Response (status + body + headers)
    Proxy-->>Client: Forward response exactly
```

<Steps>
  <Step title="Client Sends Request">
    Your frontend sends a request to your proxy server with the `X-Proxy-Auth-Key: <your-secret-key>` or `X-Authorization: <your-secret-key>` header for validation.
  </Step>

  <Step title="Proxy Validates Request">
    Your proxy validates the `X-Proxy-Auth-Key: <your-secret-key>` or `X-Authorization: <your-secret-key>` header. If invalid or missing, return an error response.
  </Step>

  <Step title="Proxy Forwards Request">
    Your proxy constructs the upstream URL, adds the `X-Plugin-Token` header, and forwards the request to your Databrain backend exactly as received.
  </Step>

  <Step title="Response Returned">
    Your proxy returns the Databrain backend response to the client exactly as received (status code, body, and headers).
  </Step>
</Steps>

## What Your Proxy Must Do

Your proxy can be built on **any backend**: Node.js, Express, FastAPI, Python, Go, Lambda, Cloudflare Worker, etc. It must receive requests from your frontend and forward them to your Databrain backend exactly as-is, with required headers added.

### 1. Read the Incoming Request

Capture all parts of the incoming request:

* Path
* Query parameters
* HTTP method
* Headers
* **Body** (JSON, text, or binary/raw)

<Warning>
  Body must **not** be modified. Preserve the raw content if it's binary (zip, pdf, excel, etc.) or text/JSON for other API calls.
</Warning>

### 2. Validate Proxy Authentication

Your proxy must read and validate either the `X-Proxy-Auth-Key` header or the `X-Authorization` header:

```
X-Proxy-Auth-Key: <your-secret-key>
```

Or:

```
X-Authorization: <your-secret-key>
```

Compare with your stored secret value. If missing or invalid, return an error:

```json theme={"dark"}
{
  "error": { "message": "missing or invalid proxy auth key" }
}
```

Return appropriate HTTP status (401 or 403).

### 3. Forward Request to Databrain Backend

#### Construct Upstream URL

```
<SELFHOSTED_BACKEND_URL>/<path>?<query>
```

#### Forward Original Method

Forward the exact HTTP method: GET, POST, PUT, PATCH, DELETE.

#### Forward Body Exactly

Do not modify the request payload in any way.

#### Required Upstream Headers

Send these headers to your Databrain backend:

| Header             | Description                            |
| ------------------ | -------------------------------------- |
| `Accept`           | Forward from incoming request          |
| `Accept-Language`  | Forward from incoming request          |
| `User-Agent`       | Forward from incoming request          |
| `Content-Type`     | Forward if present in incoming request |
| `X-Plugin-Origin`  | Forward if provided by frontend        |
| `X-Proxy-Auth-Key` | Forward original value (if provided)   |
| `X-Authorization`  | Forward original value (if provided)   |
| `X-Proxy-Auth-Url` | Forward original value                 |
| `X-Plugin-Token`   | **Your plugin token (added by proxy)** |
| `Origin`           | Forward original value                 |

### 4. Return Response Exactly

Return **exactly** what the Databrain backend returns:

* Status code
* Response body (binary or text, do not transform)
* Response headers

<Note>
  **Binary responses** (zip, pdf, xlsx) must be forwarded as-is. Do not convert binary content to text.
</Note>

## About X-Plugin-Token

The `X-Plugin-Token` header authenticates your requests with the Databrain backend. You can provide this token in two ways:

1. **A saved token** - Use a pre-generated guest token stored in your environment
2. **Generate on demand** - Call the [Databrain Guest Token API](/developer-docs/helpers/api-reference/token) to generate tokens dynamically

## Error Response Format

Every error your proxy returns should follow this format:

```json theme={"dark"}
{
  "error": { "message": "description of the error" }
}
```

**Examples:**

```json theme={"dark"}
{
  "error": { "message": "missing or invalid proxy auth key" }
}
```

```json theme={"dark"}
{
  "error": { "message": "upstream fetch failed" }
}
```

## Requirements Summary

<AccordionGroup>
  <Accordion title="Things Your Proxy MUST Do">
    * Keep method, path, and query unchanged
    * Preserve body type: JSON, text, or binary
    * Pass all required headers (including `Origin`)
    * Add `X-Plugin-Token` header
    * Validate `X-Proxy-Auth-Key` **or** `X-Authorization` before forwarding
    * Use correct error format
    * Return upstream response exactly (status, body, headers)
    * Forward binary responses (zip, pdf, xlsx) as-is
  </Accordion>

  <Accordion title="Things Your Proxy Must NOT Do">
    * Do not rewrite paths
    * Do not remove fields from body
    * Do not add custom fields to body
    * Do not modify query parameters
    * Do not transform the response
  </Accordion>
</AccordionGroup>

## Implementation Examples

<Tabs>
  <Tab title="Node.js/Express">
    ```typescript theme={"dark"}
    import express from "express";
    import fetch from "node-fetch";
    import bodyParser from "body-parser";

    const app = express();
    app.use(bodyParser.raw({ type: "*/*" })); // preserve raw body

    // --- Environment variables ---
    const PROXY_AUTH_KEY = process.env.PROXY_AUTH_KEY; // secret key for validating requests
    const SELFHOSTED_BACKEND_URL = process.env.SELFHOSTED_BACKEND_URL; // your Databrain backend
    const GUEST_TOKEN = process.env.GUEST_TOKEN; // plugin token (saved or generated via API)

    app.all('/proxy-auth/*', async (req, res) => {
      try {
        // --- Extract path and query ---
        const upstreamPath = req.path.replace(/^\/proxy-auth/, '') || '/';
        const queryString = req.originalUrl.split('?')[1] || '';
        const upstreamUrl = `${SELFHOSTED_BACKEND_URL}${upstreamPath}${queryString ? `?${queryString}` : ''}`;

        const method = req.method;
        const incomingHeaders = req.headers || {};

        // --- Handle preflight OPTIONS request ---
        if (method === 'OPTIONS') {
          return res.status(204).end();
        }

        // --- Validate proxy key ---
        const incomingProxyKey = incomingHeaders['x-proxy-auth-key'] || incomingHeaders['x-authorization'];
        if (!incomingProxyKey || incomingProxyKey !== PROXY_AUTH_KEY) {
          return res.status(401).json({
            error: { message: 'missing or invalid proxy auth key' },
          });
        }

        // --- Build upstream headers ---
        const upstreamHeaders: Record<string, any> = {
          Accept: incomingHeaders['accept'] || '*/*',
          'Accept-Language': incomingHeaders['accept-language'] || 'en-US',
          'User-Agent': incomingHeaders['user-agent'] || 'Mozilla/5.0',
          'X-Plugin-Token': GUEST_TOKEN,
        };

        if (incomingHeaders['content-type']) {
          upstreamHeaders['Content-Type'] = incomingHeaders['content-type'];
        }
        if (incomingHeaders['x-plugin-origin']) {
          upstreamHeaders['X-Plugin-Origin'] = incomingHeaders['x-plugin-origin'];
        }
        if (incomingHeaders['x-proxy-auth-url']) {
          upstreamHeaders['X-Proxy-Auth-Url'] = incomingHeaders['x-proxy-auth-url'];
        }
        if (incomingHeaders['x-proxy-auth-key']) {
          upstreamHeaders['X-Proxy-Auth-Key'] = incomingHeaders['x-proxy-auth-key'];
        }
        if (incomingHeaders['x-authorization']) {
          upstreamHeaders['X-Authorization'] = incomingHeaders['x-authorization'];
        }
        if (incomingHeaders['origin']) {
          upstreamHeaders['Origin'] = incomingHeaders['origin'];
        }

        // --- Prepare request body for non-GET/HEAD methods ---
        let upstreamBody;
        if (!['GET', 'HEAD'].includes(method)) {
          upstreamBody = req.is('application/json') ? JSON.stringify(req.body) : req.body;
        }

        // --- Forward request to backend ---
        let upstreamResponse;
        try {
          upstreamResponse = await fetch(upstreamUrl, {
            method,
            headers: upstreamHeaders,
            body: upstreamBody,
          });
        } catch (err: any) {
          return res.status(502).json({
            error: { message: 'upstream fetch failed' },
            detail: err.message,
          });
        }

        // --- Forward upstream headers as-is ---
        const outHeaders: Record<string, any> = {};
        upstreamResponse.headers.forEach((v, k) => (outHeaders[k] = v));

        // --- Handle binary vs text responses ---
        const contentType = upstreamResponse.headers.get('content-type') || '';
        if (contentType.startsWith('text/') || contentType.includes('json')) {
          const text = await upstreamResponse.text();
          res.status(upstreamResponse.status).set(outHeaders).send(text);
        } else {
          const buffer = Buffer.from(await upstreamResponse.arrayBuffer());
          res.status(upstreamResponse.status).set(outHeaders).send(buffer);
        }
      } catch (err: any) {
        return res.status(500).json({
          error: { message: 'internal server error' },
          detail: err.message,
        });
      }
    });

    // --- Start server ---
    app.listen(3000, () => {
      console.log('Proxy server running on port 3000');
    });
    ```
  </Tab>

  <Tab title="Python/FastAPI">
    ```python theme={"dark"}
    from fastapi import FastAPI, Request, Response
    from fastapi.responses import JSONResponse
    import httpx
    import os

    app = FastAPI()

    # --- Environment variables ---
    PROXY_AUTH_KEY = os.getenv("PROXY_AUTH_KEY")  # secret key for validating requests
    SELFHOSTED_BACKEND_URL = os.getenv("SELFHOSTED_BACKEND_URL")  # your Databrain backend
    GUEST_TOKEN = os.getenv("GUEST_TOKEN")  # plugin token (saved or generated via API)


    @app.api_route("/proxy-auth/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"])
    async def proxy_handler(request: Request, path: str):
        try:
            method = request.method

            # --- Handle preflight OPTIONS request ---
            if method == "OPTIONS":
                return Response(status_code=204)

            # --- Validate proxy key ---
            incoming_proxy_key = request.headers.get("x-proxy-auth-key") or request.headers.get("x-authorization")
            if not incoming_proxy_key or incoming_proxy_key != PROXY_AUTH_KEY:
                return JSONResponse(
                    status_code=401,
                    content={"error": {"message": "missing or invalid proxy auth key"}}
                )

            # --- Construct upstream URL ---
            query_string = str(request.query_params)
            upstream_url = f"{SELFHOSTED_BACKEND_URL}/{path}"
            if query_string:
                upstream_url += f"?{query_string}"

            # --- Build upstream headers ---
            upstream_headers = {
                "Accept": request.headers.get("accept", "*/*"),
                "Accept-Language": request.headers.get("accept-language", "en-US"),
                "User-Agent": request.headers.get("user-agent", "Mozilla/5.0"),
                "X-Plugin-Token": GUEST_TOKEN,
            }

            if content_type := request.headers.get("content-type"):
                upstream_headers["Content-Type"] = content_type
            if plugin_origin := request.headers.get("x-plugin-origin"):
                upstream_headers["X-Plugin-Origin"] = plugin_origin
            if proxy_auth_url := request.headers.get("x-proxy-auth-url"):
                upstream_headers["X-Proxy-Auth-Url"] = proxy_auth_url
            if proxy_auth_key := request.headers.get("x-proxy-auth-key"):
                upstream_headers["X-Proxy-Auth-Key"] = proxy_auth_key
            if x_authorization := request.headers.get("x-authorization"):
                upstream_headers["X-Authorization"] = x_authorization
            if origin := request.headers.get("origin"):
                upstream_headers["Origin"] = origin

            # --- Get request body ---
            body = await request.body() if method not in ["GET", "HEAD"] else None

            # --- Forward request to backend ---
            async with httpx.AsyncClient() as client:
                try:
                    upstream_response = await client.request(
                        method=method,
                        url=upstream_url,
                        headers=upstream_headers,
                        content=body,
                    )
                except httpx.RequestError as e:
                    return JSONResponse(
                        status_code=502,
                        content={"error": {"message": "upstream fetch failed"}, "detail": str(e)}
                    )

            # --- Forward upstream headers as-is ---
            out_headers = dict(upstream_response.headers)
            # Remove hop-by-hop headers
            for header in ["transfer-encoding", "connection", "keep-alive"]:
                out_headers.pop(header, None)

            # --- Handle binary vs text responses ---
            # Response.content is already bytes, works for both binary and text
            return Response(
                content=upstream_response.content,
                status_code=upstream_response.status_code,
                headers=out_headers,
                media_type=upstream_response.headers.get("content-type"),
            )

        except Exception as e:
            return JSONResponse(
                status_code=500,
                content={"error": {"message": "internal server error"}, "detail": str(e)}
            )


    if __name__ == "__main__":
        import uvicorn
        uvicorn.run(app, host="0.0.0.0", port=3000)
    ```
  </Tab>

  <Tab title="Python/Flask">
    ```python theme={"dark"}
    from flask import Flask, request, Response, jsonify
    import requests
    import os

    app = Flask(__name__)

    # --- Environment variables ---
    PROXY_AUTH_KEY = os.getenv("PROXY_AUTH_KEY")  # secret key for validating requests
    SELFHOSTED_BACKEND_URL = os.getenv("SELFHOSTED_BACKEND_URL")  # your Databrain backend
    GUEST_TOKEN = os.getenv("GUEST_TOKEN")  # plugin token (saved or generated via API)


    @app.route("/proxy-auth/<path:path>", methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"])
    def proxy_handler(path):
        try:
            method = request.method

            # --- Handle preflight OPTIONS request ---
            if method == "OPTIONS":
                return Response(status=204)

            # --- Validate proxy key ---
        incoming_proxy_key = request.headers.get("X-Proxy-Auth-Key") or request.headers.get("X-Authorization")
            if not incoming_proxy_key or incoming_proxy_key != PROXY_AUTH_KEY:
            return jsonify({"error": {"message": "missing or invalid proxy auth key"}}), 401

            # --- Construct upstream URL ---
            query_string = request.query_string.decode("utf-8")
            upstream_url = f"{SELFHOSTED_BACKEND_URL}/{path}"
            if query_string:
                upstream_url += f"?{query_string}"

            # --- Build upstream headers ---
            upstream_headers = {
                "Accept": request.headers.get("Accept", "*/*"),
                "Accept-Language": request.headers.get("Accept-Language", "en-US"),
                "User-Agent": request.headers.get("User-Agent", "Mozilla/5.0"),
                "X-Plugin-Token": GUEST_TOKEN,
            }

            if content_type := request.headers.get("Content-Type"):
                upstream_headers["Content-Type"] = content_type
            if plugin_origin := request.headers.get("X-Plugin-Origin"):
                upstream_headers["X-Plugin-Origin"] = plugin_origin
            if proxy_auth_url := request.headers.get("X-Proxy-Auth-Url"):
                upstream_headers["X-Proxy-Auth-Url"] = proxy_auth_url
            if proxy_auth_key := request.headers.get("X-Proxy-Auth-Key"):
                upstream_headers["X-Proxy-Auth-Key"] = proxy_auth_key
        if x_authorization := request.headers.get("X-Authorization"):
            upstream_headers["X-Authorization"] = x_authorization
            if origin := request.headers.get("Origin"):
                upstream_headers["Origin"] = origin

            # --- Get request body ---
            body = request.get_data() if method not in ["GET", "HEAD"] else None

            # --- Forward request to backend ---
            try:
                upstream_response = requests.request(
                    method=method,
                    url=upstream_url,
                    headers=upstream_headers,
                    data=body,
                )
            except requests.RequestException as e:
                return jsonify({"error": {"message": "upstream fetch failed"}, "detail": str(e)}), 502

            # --- Forward upstream headers as-is ---
            out_headers = dict(upstream_response.headers)
            # Remove hop-by-hop headers
            for header in ["Transfer-Encoding", "Connection", "Keep-Alive"]:
                out_headers.pop(header, None)

            # --- Handle binary vs text responses ---
            # upstream_response.content is already bytes, works for both binary and text
            return Response(
                response=upstream_response.content,
                status=upstream_response.status_code,
                headers=out_headers,
                mimetype=upstream_response.headers.get("Content-Type"),
            )

        except Exception as e:
            return jsonify({"error": {"message": "internal server error"}, "detail": str(e)}), 500


    if __name__ == "__main__":
        app.run(host="0.0.0.0", port=3000)
    ```
  </Tab>

  <Tab title="Java/Spring Boot">
    ```java theme={"dark"}
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.http.*;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.client.RestTemplate;
    import org.springframework.web.client.RestClientException;

    import jakarta.servlet.http.HttpServletRequest;
    import java.io.IOException;
    import java.util.Map;
    import java.util.stream.Collectors;

    @RestController
    @RequestMapping("/proxy-auth")
    public class DatabrainProxyController {

        @Value("${PROXY_AUTH_KEY}")
        private String proxyAuthKey;

        @Value("${SELFHOSTED_BACKEND_URL}")
        private String selfhostedBackendUrl;

        @Value("${GUEST_TOKEN}")
        private String guestToken;

        private final RestTemplate restTemplate = new RestTemplate();

        @RequestMapping(value = "/**", method = {
            RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT,
            RequestMethod.PATCH, RequestMethod.DELETE, RequestMethod.OPTIONS
        })
        public ResponseEntity<?> proxyHandler(
                HttpServletRequest request,
                @RequestBody(required = false) byte[] body,
                @RequestHeader Map<String, String> headers
        ) {
            try {
                String method = request.getMethod();

                // --- Handle preflight OPTIONS request ---
                if ("OPTIONS".equals(method)) {
                    return ResponseEntity.status(204).build();
                }

                // --- Validate proxy key ---
                String incomingProxyKey = headers.getOrDefault("x-proxy-auth-key", headers.get("x-authorization"));
                if (incomingProxyKey == null || !incomingProxyKey.equals(proxyAuthKey)) {
                    return ResponseEntity.status(401).body(Map.of(
                        "error", Map.of("message", "missing or invalid proxy auth key")
                    ));
                }

                // --- Construct upstream URL ---
                String path = request.getRequestURI().replaceFirst("^/proxy-auth", "");
                String queryString = request.getQueryString();
                String upstreamUrl = selfhostedBackendUrl + path;
                if (queryString != null && !queryString.isEmpty()) {
                    upstreamUrl += "?" + queryString;
                }

                // --- Build upstream headers ---
                HttpHeaders upstreamHeaders = new HttpHeaders();
                upstreamHeaders.set("Accept", headers.getOrDefault("accept", "*/*"));
                upstreamHeaders.set("Accept-Language", headers.getOrDefault("accept-language", "en-US"));
                upstreamHeaders.set("User-Agent", headers.getOrDefault("user-agent", "Mozilla/5.0"));
                upstreamHeaders.set("X-Plugin-Token", guestToken);

                if (headers.containsKey("content-type")) {
                    upstreamHeaders.set("Content-Type", headers.get("content-type"));
                }
                if (headers.containsKey("x-plugin-origin")) {
                    upstreamHeaders.set("X-Plugin-Origin", headers.get("x-plugin-origin"));
                }
                if (headers.containsKey("x-proxy-auth-url")) {
                    upstreamHeaders.set("X-Proxy-Auth-Url", headers.get("x-proxy-auth-url"));
                }
                if (headers.containsKey("x-proxy-auth-key")) {
                    upstreamHeaders.set("X-Proxy-Auth-Key", headers.get("x-proxy-auth-key"));
                }
                if (headers.containsKey("x-authorization")) {
                    upstreamHeaders.set("X-Authorization", headers.get("x-authorization"));
                }
                if (headers.containsKey("origin")) {
                    upstreamHeaders.set("Origin", headers.get("origin"));
                }

                // --- Prepare request entity ---
                HttpEntity<byte[]> requestEntity = new HttpEntity<>(
                    "GET".equals(method) || "HEAD".equals(method) ? null : body,
                    upstreamHeaders
                );

                // --- Forward request to backend ---
                ResponseEntity<byte[]> upstreamResponse;
                try {
                    upstreamResponse = restTemplate.exchange(
                        upstreamUrl,
                        HttpMethod.valueOf(method),
                        requestEntity,
                        byte[].class
                    );
                } catch (RestClientException e) {
                    return ResponseEntity.status(502).body(Map.of(
                        "error", Map.of("message", "upstream fetch failed"),
                        "detail", e.getMessage()
                    ));
                }

                // --- Forward upstream headers as-is ---
                HttpHeaders outHeaders = new HttpHeaders();
                upstreamResponse.getHeaders().forEach((key, values) -> {
                    // Skip hop-by-hop headers
                    if (!key.equalsIgnoreCase("Transfer-Encoding") &&
                        !key.equalsIgnoreCase("Connection") &&
                        !key.equalsIgnoreCase("Keep-Alive")) {
                        outHeaders.put(key, values);
                    }
                });

                // --- Handle binary vs text responses ---
                // byte[] response body works for both binary and text content
                return ResponseEntity
                    .status(upstreamResponse.getStatusCode())
                    .headers(outHeaders)
                    .body(upstreamResponse.getBody());

            } catch (Exception e) {
                return ResponseEntity.status(500).body(Map.of(
                    "error", Map.of("message", "internal server error"),
                    "detail", e.getMessage()
                ));
            }
        }
    }
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={"dark"}
    package main

    import (
    	"io"
    	"log"
    	"net/http"
    	"os"
    	"strings"
    )

    var (
    	proxyAuthKey         = os.Getenv("PROXY_AUTH_KEY")
    	selfhostedBackendURL = os.Getenv("SELFHOSTED_BACKEND_URL")
    	guestToken           = os.Getenv("GUEST_TOKEN")
    )

    func proxyHandler(w http.ResponseWriter, r *http.Request) {
    	// --- Handle preflight OPTIONS request ---
    	if r.Method == "OPTIONS" {
    		w.WriteHeader(http.StatusNoContent)
    		return
    	}

    	// --- Validate proxy key ---
    	incomingProxyKey := r.Header.Get("X-Proxy-Auth-Key")
    	if incomingProxyKey == "" {
    		incomingProxyKey = r.Header.Get("X-Authorization")
    	}
    	if incomingProxyKey == "" || incomingProxyKey != proxyAuthKey {
    		w.Header().Set("Content-Type", "application/json")
    		w.WriteHeader(http.StatusUnauthorized)
    		w.Write([]byte(`{"error":{"message":"missing or invalid proxy auth key"}}`))
    		return
    	}

    	// --- Construct upstream URL ---
    	path := strings.TrimPrefix(r.URL.Path, "/proxy-auth")
    	if path == "" {
    		path = "/"
    	}
    	upstreamURL := selfhostedBackendURL + path
    	if r.URL.RawQuery != "" {
    		upstreamURL += "?" + r.URL.RawQuery
    	}

    	// --- Read request body ---
    	var body io.Reader
    	if r.Method != "GET" && r.Method != "HEAD" {
    		body = r.Body
    		defer r.Body.Close()
    	}

    	// --- Create upstream request ---
    	upstreamReq, err := http.NewRequest(r.Method, upstreamURL, body)
    	if err != nil {
    		w.Header().Set("Content-Type", "application/json")
    		w.WriteHeader(http.StatusInternalServerError)
    		w.Write([]byte(`{"error":{"message":"internal server error"}}`))
    		return
    	}

    	// --- Build upstream headers ---
    	upstreamReq.Header.Set("Accept", getOrDefault(r.Header.Get("Accept"), "*/*"))
    	upstreamReq.Header.Set("Accept-Language", getOrDefault(r.Header.Get("Accept-Language"), "en-US"))
    	upstreamReq.Header.Set("User-Agent", getOrDefault(r.Header.Get("User-Agent"), "Mozilla/5.0"))
    	upstreamReq.Header.Set("X-Plugin-Token", guestToken)

    	if ct := r.Header.Get("Content-Type"); ct != "" {
    		upstreamReq.Header.Set("Content-Type", ct)
    	}
    	if po := r.Header.Get("X-Plugin-Origin"); po != "" {
    		upstreamReq.Header.Set("X-Plugin-Origin", po)
    	}
    	if pau := r.Header.Get("X-Proxy-Auth-Url"); pau != "" {
    		upstreamReq.Header.Set("X-Proxy-Auth-Url", pau)
    	}
    	if pak := r.Header.Get("X-Proxy-Auth-Key"); pak != "" {
    		upstreamReq.Header.Set("X-Proxy-Auth-Key", pak)
    	}
    	if xa := r.Header.Get("X-Authorization"); xa != "" {
    		upstreamReq.Header.Set("X-Authorization", xa)
    	}
    	if origin := r.Header.Get("Origin"); origin != "" {
    		upstreamReq.Header.Set("Origin", origin)
    	}

    	// --- Forward request to backend ---
    	client := &http.Client{}
    	upstreamResp, err := client.Do(upstreamReq)
    	if err != nil {
    		w.Header().Set("Content-Type", "application/json")
    		w.WriteHeader(http.StatusBadGateway)
    		w.Write([]byte(`{"error":{"message":"upstream fetch failed"},"detail":"` + err.Error() + `"}`))
    		return
    	}
    	defer upstreamResp.Body.Close()

    	// --- Forward upstream headers as-is ---
    	for key, values := range upstreamResp.Header {
    		// Skip hop-by-hop headers
    		lowerKey := strings.ToLower(key)
    		if lowerKey == "transfer-encoding" || lowerKey == "connection" || lowerKey == "keep-alive" {
    			continue
    		}
    		for _, value := range values {
    			w.Header().Add(key, value)
    		}
    	}

    	// --- Forward upstream response (handles both binary and text) ---
    	w.WriteHeader(upstreamResp.StatusCode)
    	io.Copy(w, upstreamResp.Body)
    }

    func getOrDefault(value, defaultValue string) string {
    	if value == "" {
    		return defaultValue
    	}
    	return value
    }

    func main() {
    	http.HandleFunc("/proxy-auth/", proxyHandler)
    	log.Println("Proxy server running on port 3000")
    	log.Fatal(http.ListenAndServe(":3000", nil))
    }
    ```
  </Tab>
</Tabs>

## Frontend Configuration

Configure your frontend to send requests through your proxy server instead of directly to the Databrain backend.

### Configuration Setup

<CodeGroup>
  ```javascript React theme={"dark"}
  import { Databrain } from '@databrainhq/plugin';

  // Configure proxy authentication globally
  if (typeof window !== 'undefined') {
    window.dbn = {
      proxyAuthUrl: 'https://your-proxy-server.com/proxy-auth',
      proxyAuthKey: 'your-proxy-authentication-key',
      isEnableProxyAuth: true
    };
  }

  function MyDashboard() {
    return (
      <Databrain
        dashboardId="your-dashboard-id"
        // No token prop needed - proxy handles authentication
      />
    );
  }
  ```

  ```javascript Vue.js theme={"dark"}
  <template>
    <div id="databrain-container"></div>
  </template>

  <script>
  export default {
    name: 'DataBrainDashboard',
    mounted() {
      // Configure proxy authentication
      window.dbn = {
        proxyAuthUrl: 'https://your-proxy-server.com/proxy-auth',
        proxyAuthKey: 'your-proxy-authentication-key',
        isEnableProxyAuth: true
      };

      // Initialize DataBrain component
    }
  }
  </script>
  ```

  ```html Vanilla JavaScript theme={"dark"}
  <!DOCTYPE html>
  <html>
  <head>
    <script src="https://cdn.usedatabrain.com/plugin/latest/databrain.min.js"></script>
  </head>
  <body>
    <div id="databrain-dashboard"></div>
    
    <script>
      // Configure proxy authentication
      window.dbn = {
        proxyAuthUrl: 'https://your-proxy-server.com/proxy-auth',
        proxyAuthKey: 'your-proxy-authentication-key',
        isEnableProxyAuth: true
      };

      // Initialize DataBrain
      const dashboard = new Databrain({
        dashboardId: 'your-dashboard-id',
        container: document.getElementById('databrain-dashboard')
      });
    </script>
  </body>
  </html>
  ```
</CodeGroup>

<Tip>
  **TypeScript Users:** You need to declare the global `window.dbn` type in your project:

  ```typescript theme={"dark"}
  declare global {
    interface Window {
      dbn?: {
        proxyAuthUrl?: string;
        proxyAuthKey?: string;
        isEnableProxyAuth?: boolean;
      };
    }
  }
  ```
</Tip>

### Configuration Properties

| Property                       | Type      | Required | Description                                                                                                                              |
| ------------------------------ | --------- | -------: | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `window.dbn.proxyAuthUrl`      | `string`  |      Yes | The full URL of your proxy endpoint. This is sent as the `X-Proxy-Auth-Url` header. Example: `https://your-proxy-server.com/proxy-auth`. |
| `window.dbn.proxyAuthKey`      | `string`  |      Yes | Secret key your frontend sends as `X-Proxy-Auth-Key` (or `X-Authorization`) for your proxy to validate.                                  |
| `window.dbn.isEnableProxyAuth` | `boolean` |      Yes | Enables proxy authentication mode. Use `true` to enable proxy auth. Use `false` (or omit) to use direct token authentication.            |

<Tip>
  Generate a strong, random key and store it securely. This key must match the one your proxy expects.
</Tip>

## Environment Variables

Your proxy server requires these environment variables:

| Variable                 | Description                                                                                   |
| ------------------------ | --------------------------------------------------------------------------------------------- |
| `PROXY_AUTH_KEY`         | Secret key to validate incoming requests (must match frontend `proxyAuthKey`)                 |
| `SELFHOSTED_BACKEND_URL` | Your Databrain backend URL (e.g., `https://api.usedatabrain.com`)                             |
| `GUEST_TOKEN`            | Your plugin token (saved or [generated via API](/developer-docs/helpers/api-reference/token)) |

## Security Best Practices

<AccordionGroup>
  <Accordion title="Authentication">
    * **Use Strong Keys:** Generate a cryptographically random proxy authentication key
    * **Validate Every Request:** Always verify the `X-Proxy-Auth-Key` header
    * **HTTPS Only:** Ensure your proxy endpoint is only accessible via HTTPS
    * **Rotate Keys:** Periodically rotate your proxy authentication keys
  </Accordion>

  <Accordion title="Rate Limiting">
    Add rate limiting to prevent abuse:

    * Limit requests per IP address
    * Limit requests per user/session
    * Implement exponential backoff for repeated failures
  </Accordion>

  <Accordion title="Logging & Monitoring">
    * Log all proxy requests for audit purposes
    * Monitor for unusual traffic patterns
    * Set up alerts for authentication failures
  </Accordion>

  <Accordion title="Token Management">
    * Store `GUEST_TOKEN` securely in environment variables
    * Never expose tokens in client-side code
    * Consider generating tokens dynamically for enhanced security
  </Accordion>
</AccordionGroup>

## Error Handling

### Common Errors

<AccordionGroup>
  <Accordion title="401 - Missing or Invalid Proxy Key">
    **Error:** `{"error": {"message": "missing or invalid X-Proxy-Auth-Key"}}`

    **Cause:** The `X-Proxy-Auth-Key` header is missing or doesn't match.

    **Solution:** Verify that `window.dbn.proxyAuthKey` matches your proxy's `PROXY_AUTH_KEY` environment variable.
  </Accordion>

  <Accordion title="502 - Upstream Fetch Failed">
    **Error:** `{"error": {"message": "upstream fetch failed"}}`

    **Cause:** Your proxy cannot reach the Databrain backend.

    **Solution:**

    * Verify `SELFHOSTED_BACKEND_URL` is correct
    * Check network connectivity from your proxy server
    * Verify firewall rules allow outbound connections
  </Accordion>

  <Accordion title="500 - Internal Server Error">
    **Error:** `{"error": {"message": "internal server error"}}`

    **Cause:** An unexpected error occurred in your proxy.

    **Solution:** Check your proxy server logs for detailed error information.
  </Accordion>
</AccordionGroup>

## Testing Your Proxy

Use curl to test your proxy endpoint:

```bash theme={"dark"}
# Test with valid proxy key (X-Proxy-Auth-Key)
curl -X GET "https://your-proxy-server.com/proxy-auth/api/health" \
  -H "X-Proxy-Auth-Key: your-proxy-key"

# Test with valid proxy key (X-Authorization)
curl -X GET "https://your-proxy-server.com/proxy-auth/api/health" \
  -H "X-Authorization: your-proxy-key"

# Test authentication validation (should return 401)
curl -X GET "https://your-proxy-server.com/proxy-auth/api/health" \
  -H "X-Proxy-Auth-Key: invalid-key"

# Test authentication validation (should return 401)
curl -X GET "https://your-proxy-server.com/proxy-auth/api/health" \
  -H "X-Authorization: invalid-key"
```

## Related Resources

<CardGroup cols={2}>
  <Card title="Guest Token API" icon="ticket" href="/developer-docs/helpers/api-reference/token">
    Learn about generating guest tokens for your proxy
  </Card>

  <Card title="Component Options" icon="sliders" href="/developer-docs/helpers/component-options-reference">
    Explore all DataBrain component configuration options
  </Card>

  <Card title="Authentication Guide" icon="shield" href="/developer-docs/token">
    Comprehensive guide to DataBrain authentication
  </Card>

  <Card title="Security Best Practices" icon="lock" href="/developer-docs/multi-tenant-access-control">
    Learn about multi-tenant access control and security
  </Card>
</CardGroup>
