Jamdesk Documentation logo

Reverse Proxy

Serve your documentation at /docs using nginx, Apache, Caddy, Traefik, or HAProxy. Includes tested configuration snippets for each reverse proxy.

Add a location/route block to nginx, Apache, Caddy, Traefik, or HAProxy that forwards /docs traffic to your Jamdesk subdomain.

Prerequisites

  • Access to your web server configuration
  • Your Jamdesk subdomain (found in dashboard settings)

Using a custom subpath instead of the default /docs? Replace /docs with your subpath in every location/route block below. Update the configuration again after you rename the subpath in the dashboard.

nginx

Add a location block to proxy /docs requests to Jamdesk:

nginx.conf
server {
    listen 443 ssl;
    server_name yoursite.com;

    # Your existing configuration...

    # Proxy /docs to Jamdesk
    location /docs {
        proxy_pass https://YOUR_SLUG.jamdesk.app;
        proxy_ssl_server_name on;

        proxy_set_header Host YOUR_SLUG.jamdesk.app;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        # Required for domain verification
        proxy_set_header X-Jamdesk-Forwarded-Host $host;
    }

    # Next.js static assets (JS, CSS)
    location /_next/ {
        proxy_pass https://YOUR_SLUG.jamdesk.app;
        proxy_ssl_server_name on;
        proxy_set_header Host YOUR_SLUG.jamdesk.app;
    }

    # Jamdesk assets (fonts, images, branding)
    location /_jd/ {
        proxy_pass https://YOUR_SLUG.jamdesk.app;
        proxy_ssl_server_name on;
        proxy_set_header Host YOUR_SLUG.jamdesk.app;
    }

}

Replace YOUR_SLUG with your Jamdesk subdomain.

Path handling matters. The proxy_pass URL has no trailing path. nginx keeps the original request path. A request to /docs/page proxies to jamdesk.app/docs/page. A trailing slash (proxy_pass https://...jamdesk.app/) strips the /docs prefix. Keep it exactly as shown above.

Reload nginx:

sudo nginx -t && sudo systemctl reload nginx

Apache

Use mod_proxy to forward /docs requests to Jamdesk:

httpd.conf or .htaccess
<VirtualHost *:443>
    ServerName yoursite.com

    # Your existing configuration...

    # Enable proxy modules
    ProxyRequests Off
    SSLProxyEngine On

    # Proxy /docs to Jamdesk
    ProxyPass /docs https://YOUR_SLUG.jamdesk.app/docs
    ProxyPassReverse /docs https://YOUR_SLUG.jamdesk.app/docs

    # Next.js static assets (JS, CSS)
    ProxyPass /_next https://YOUR_SLUG.jamdesk.app/_next
    ProxyPassReverse /_next https://YOUR_SLUG.jamdesk.app/_next

    # Jamdesk assets (fonts, images, branding)
    ProxyPass /_jd https://YOUR_SLUG.jamdesk.app/_jd
    ProxyPassReverse /_jd https://YOUR_SLUG.jamdesk.app/_jd

    <Location /docs>
        RequestHeader set X-Forwarded-Host "yoursite.com"
        RequestHeader set X-Forwarded-Proto "https"
        # Required for domain verification
        RequestHeader set X-Jamdesk-Forwarded-Host "yoursite.com"
    </Location>
</VirtualHost>

Enable the required modules:

sudo a2enmod proxy proxy_http ssl headers
sudo systemctl reload apache2

Caddy

Caddy sets up HTTPS on its own. Add these handle blocks:

Caddyfile
yoursite.com {
    # Your existing configuration...

    handle /docs* {
        reverse_proxy https://YOUR_SLUG.jamdesk.app {
            header_up Host {upstream_hostport}
            header_up X-Forwarded-Host {host}
            # Required for domain verification
            header_up X-Jamdesk-Forwarded-Host {host}
        }
    }

    # Next.js static assets and Jamdesk assets
    handle /_next/* {
        reverse_proxy https://YOUR_SLUG.jamdesk.app {
            header_up Host {upstream_hostport}
        }
    }

    handle /_jd/* {
        reverse_proxy https://YOUR_SLUG.jamdesk.app {
            header_up Host {upstream_hostport}
        }
    }

    # Handle other routes
    handle {
        # Your main site configuration
    }
}

Reload Caddy after changes:

sudo systemctl reload caddy

Traefik

Configure a Traefik router and service:

traefik.yml
http:
  routers:
    docs-router:
      rule: "Host(`yoursite.com`) && (PathPrefix(`/docs`) || PathPrefix(`/_next`) || PathPrefix(`/_jd`))"
      service: jamdesk-docs
      middlewares:
        - jamdesk-headers
      tls: {}

  middlewares:
    jamdesk-headers:
      headers:
        customRequestHeaders:
          # Required for domain verification
          X-Jamdesk-Forwarded-Host: "yoursite.com"

  services:
    jamdesk-docs:
      loadBalancer:
        servers:
          - url: "https://YOUR_SLUG.jamdesk.app"
        passHostHeader: false

HAProxy

Add HAProxy backend and ACL rules:

haproxy.cfg
frontend https
    bind *:443 ssl crt /etc/ssl/certs/yoursite.pem

    # Route /docs and assets to Jamdesk backend
    acl is_docs path_beg /docs
    acl is_next path_beg /_next
    acl is_jd path_beg /_jd
    use_backend jamdesk_docs if is_docs or is_next or is_jd

    # Default backend for other requests
    default_backend main_site

backend jamdesk_docs
    server jamdesk YOUR_SLUG.jamdesk.app:443 ssl verify none
    http-request set-header Host YOUR_SLUG.jamdesk.app
    http-request set-header X-Forwarded-Host %[req.hdr(host)]
    # Required for domain verification
    http-request set-header X-Jamdesk-Forwarded-Host %[req.hdr(host)]

Forward Your Root Files Too

robots.txt, sitemap.xml, llms.txt, and llms-full.txt live at the domain root, not under /docs. A proxy that forwards only /docs, /_next, and /_jd leaves these to your own site, or leaves them missing. That breaks AI-agent discovery and costs you SEO.

Add the location/route pattern you used for /docs for each of these four paths. For nginx:

nginx.conf
    location = /robots.txt {
        proxy_pass https://YOUR_SLUG.jamdesk.app;
        proxy_ssl_server_name on;
        proxy_set_header Host YOUR_SLUG.jamdesk.app;
        proxy_set_header X-Jamdesk-Forwarded-Host $host;
    }

    location = /sitemap.xml {
        proxy_pass https://YOUR_SLUG.jamdesk.app;
        proxy_ssl_server_name on;
        proxy_set_header Host YOUR_SLUG.jamdesk.app;
        proxy_set_header X-Jamdesk-Forwarded-Host $host;
    }

    location = /llms.txt {
        proxy_pass https://YOUR_SLUG.jamdesk.app;
        proxy_ssl_server_name on;
        proxy_set_header Host YOUR_SLUG.jamdesk.app;
        proxy_set_header X-Jamdesk-Forwarded-Host $host;
    }

    location = /llms-full.txt {
        proxy_pass https://YOUR_SLUG.jamdesk.app;
        proxy_ssl_server_name on;
        proxy_set_header Host YOUR_SLUG.jamdesk.app;
        proxy_set_header X-Jamdesk-Forwarded-Host $host;
    }

For Apache, Caddy, Traefik, or HAProxy, copy your /docs block and repeat it once per path. Keep the same headers and upstream.

Only forward these if Jamdesk owns your domain root. If your main site already serves its own robots.txt or sitemap.xml, merge the two instead of overwriting one with the other.

Required Headers

Set these headers on any proxy:

HeaderValuePurpose
HostYOUR_SLUG.jamdesk.appIdentifies the request to Jamdesk
X-Forwarded-HostYour domainTells Jamdesk which domain to use in URLs
X-Forwarded-ProtohttpsEnsures secure URL generation
X-Jamdesk-Forwarded-HostYour domainRequired for domain verification
?jd_proxy=1 query markerAppended to the upstream URLAlternative to the header, for tools that can only rewrite a URL and can't set request headers. See Custom domain only

The X-Jamdesk-Forwarded-Host header (or the ?jd_proxy=1 marker) is required. Leaving out both fails quietly.

Requests still succeed and nothing errors. Here is what breaks:

  • No custom domain registered in the dashboard: Jamdesk serves your pages with <meta name="robots" content="noindex">. Canonical, Open Graph, and sitemap URLs point at YOUR_SLUG.jamdesk.app instead of your domain. Search engines will never index your documentation.
  • Custom domain registered: Jamdesk falls back to the domain on file, and your URLs stay correct. Jamdesk still can't tell proxy traffic apart from direct traffic to your *.jamdesk.app subdomain. The Custom domain only preflight check will fail.

A 403 means the header is present but names a domain that isn't registered and active for this project.

The proxy configuration is a one-time setup. Changing your custom domain or configuration in the Jamdesk dashboard needs no proxy update. Jamdesk makes routing decisions server-side from your dashboard settings.

Troubleshooting

Verify the proxy can reach YOUR_SLUG.jamdesk.app over HTTPS. Check firewall rules and DNS resolution.

Enable SSL/TLS for the upstream connection. For nginx, add proxy_ssl_server_name on;. For Apache, enable SSLProxyEngine On.

Check the X-Forwarded-Host header. Jamdesk uses it to build asset URLs and internal links.

Check that your proxy isn't following redirects. It should forward the response as-is.

Seeing "Domain is not authorized to serve this content"? Work through these checks:

  1. Verify your domain is registered in the Jamdesk dashboard
  2. Complete DNS verification (TXT record) for your domain
  3. Set the X-Jamdesk-Forwarded-Host header in your proxy configuration
  4. Check that your domain maps to the correct project

The domain must be verified before the proxy can serve documentation.

Preflight fetches /_jd/preflight on your live domain and checks what reached Jamdesk. "Doesn't identify itself" means a request reached Jamdesk without the header or the marker. Recheck the X-Jamdesk-Forwarded-Host line in every location/route block above. See Custom domain only for what each preflight message means.

What's Next?

Custom Domain Only

Stop your subdomain from answering directly

Custom Domains

Verify DNS and troubleshoot

Subpath Hosting

Serve docs at /docs