🕐 8 min read
If you are building a web application that both embeds a Tableau viz and calls VizQL Data Service (VDS) from the same browser page, you need to configure your app so that the browser can attach the Tableau session cookie to both the embedded viz requests and the VDS API calls. This topic explains why that is challenging, and how to solve it using a reverse proxy, or a wildcard cookie domain (on Tableau Server only).
When a user authenticates with Tableau—either by loading an embedded viz or by signing in directly—Tableau sets an HttpOnly session cookie in the browser (that is, the workgroup_session_id on Tableau Server and Tableau Cloud). This cookie:
This means that if your web application lives on a different domain than Tableau, VDS calls from the browser will not include the session cookie, and authentication will fail.
The Domain and Path attributes in the Set-Cookie header control which URLs receive a cookie. To get the embedded viz and VDS calls to share the Tableau session cookie, you configure your setup so that both run under the same cookie scope. The right approach depends upon your deployment.
The most reliable solution for Tableau Cloud and Tableau Server deployments is to set up a reverse proxy. This approach is recommended where the web app and Tableau are on different domains. For more information about this setup and the requirements, see How the reverse proxy works.
If you have a deployment where Tableau Server and the web app are on different hosts, but are in the same domain, you have another option. You can set the session cookie on a wildcard domain, which lets you avoid setting up a reverse proxy.
Set up a wildcard cookie domain on Tableau Server
Setting up a reverse proxy is the most reliable way to enable the embedded viz and the VDS calls to share the same Tableau session cookie. This section shows an example setup that uses a Node.js backend deployed to Heroku, with the Nginx buildpack as a reverse proxy. See How the reverse proxy works for more information about reverse proxies.
For more information, see Assign API access capability.
kid: your Secret IDiss: your Client IDsub: the Tableau user’s emailaud: tableauscp: ["tableau:views:embed", "tableau:viz_data_service:read"]exp: short expiry (5 minutes recommended)For more information on using a JWT with VDS, see Configure authentication.
For instructions, see Find the workbook session ID and workbook data source ID.
The Node.js server needs to handle two things:
index.html with the embedded viz and a button to trigger the VDS query.POST /node/query) that:
X-Session-Id and Global-Session-Header.A minimal VDS request body looks like this:
{
"datasource": {
"workbookDatasourceId": "1a2a3b4b-5c6c-7d8d-9e0e-1f2f3a4a5b6b"
},
"query": {
"fields": [
{ "fieldCaption": "State/Province" }
]
}
}
For information about building queries, see Query a Data Source.
Create your Nginx configuration file (for example, config/nginx.conf.erb) with the following three routing rules:
# Serve index.html and other static assets from Node.js
location = / {
proxy_pass http://localhost:;
}
# Route VDS proxy calls to Node.js backend
location /node/ {
proxy_pass http://localhost:;
}
# Proxy all other requests (viz, auth) to Tableau Cloud
location / {
proxy_pass https://;
proxy_set_header Host ;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
This ensures that the viz is loaded through your app domain, so the Tableau session cookie is scoped to your app domain. When the browser then calls /node/query, the same cookie is automatically attached.
In the web app, in this example, index.html, embed the viz using the Tableau Embedding API v3, pointing src at your app domain and the viz path (Nginx proxies it to Tableau Cloud). This web app also has a button that sends a VDS query to Tableau when clicked.
<tableau-viz
src="https://views/YourWorkbook/YourView"
token="">
</tableau-viz>
<!-- add a button to call VDS --->
<div id="vizQL">
<h2>VizQL Data Service</h2>
<button type="button" id="query-btn" disabled="true">Call VizQL Data Service</button>
<pre id="results"></pre>
</div>
Add a button that calls your VDS proxy endpoint and displays the result. Include the X-Session-Id and Global-Session-Header in the call. For instructions on finding those values, see Find the workbook session ID and workbook data source ID
document.getElementById('query-btn').addEventListener('click', async () => {
const response = await fetch('/node/query', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'X-Session-Id': vizqlServerSessionId,
'Global-Session-Header': globalSessionHeader
},
});
const data = await response.json();
document.getElementById('results').textContent = JSON.stringify(data, null, 2);
});
Note: The credentials: 'include' option is required to ensure the browser includes cookies when calling its own origin.
/node/query endpoint, and inspect the Cookie header. The workgroup_session_id (or Tableau session) cookie domain should match your app domain. This confirms that cookie sharing is working correctly.Setting up a reverse proxy is the most reliable way to enable the embedded viz and the VDS calls to share the same Tableau session cookie. This section shows an example setup that uses Nginx to place the Tableau Server, a web server (Node.js), and the VDS API calls all under one public hostname. All hosts must be within the same network. DNS configuration is managed by your administrator. See How the reverse proxy works for more information about reverse proxies.
Run the following TSM commands to configure Tableau Server to accept requests through the proxy (replace analytics.example.com with the name of your host):
tsm configuration set -k gateway.public.host -v "analytics.example.com"
tsm configuration set -k gateway.public.port -v 443
tsm configuration set -k gateway.trusted -v <ip-address-of-nginx-server>
tsm configuration set -k gateway.trusted_hosts -v "analytics.example.com"
tsm pending-changes apply
Configure SSL on Tableau Server using the same certificates as the proxy. For more information, see Configure SSL for External HTTP Traffic to and from Tableau Server and Configure Tableau Server to Work with a Reverse Proxy Server.
The Node.js server needs to:
index.html with the embedded viz.GET /api/query) that reads the Tableau session cookie forwarded from the browser and uses it to call VDS on Tableau Server.Make sure the Node.js host can resolve the hostname of the Tableau Server, either via an /etc/hosts entry or DNS lookup.
Configure Nginx at analytics.example.com with the following routing rules:
# Route all Tableau Server requests (viz, auth, REST API)
location / {
proxy_pass https://<tableau-server-ip>;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Route the embedding app
location /app/ {
proxy_pass https://<embedding-server-ip>/;
proxy_set_header Host $host;
}
# Route VDS calls to the Node.js backend
location /app/api/ {
proxy_pass https://<embedding-server-ip>/api/;
proxy_set_header Host $host;
}
Important: Tableau Server sign-in redirects to /sign_in. Make sure your Nginx configuration includes a location entry that handles this redirect path. Missing this is a common configuration failure point.
Make the VDS call using credentials: 'include' to ensure the browser sends the session cookie:
fetch('/app/api/query', { credentials: 'include' })
.then(res => res.json())
.then(data => console.log(data));
Note: On Tableau Server, you could also use credentials: 'same-origin' to ensure the browser sends the session cookie.
In Chrome DevTools, open the Network tab. Find the /app/api/query request and confirm that the Cookie header contains workgroup_session_id with domain analytics.example.com.
If your web app and Tableau Server are on different hosts, but in same domain, you can configure Tableau to set the session cookie on a wildcard domain. This allows all subdomains under a shared parent to receive the cookie automatically, without needing a reverse proxy for the back-end server.
Run the following TSM commands (replace .example.com with the parent domain of your network. Be sure to include the leading period (.)).
tsm configuration set -k vizportal.session_cookie_domain -v ".example.com"
tsm pending-changes apply
With this configuration, cookies set by tableau.example.com are automatically available to webapp.example.com and any other *.example.com subdomain. Some configurations that have been tested and confirmed to work:
| Web app server | Tableau Server | Cookie domain setting | Result |
|---|---|---|---|
test.example.com |
analytics.example.com |
.example.com |
Works |
test.analytics.example.com |
analytics.example.com |
analytics.example.com |
Works |
test.example.com |
analytics.otherdomain.com |
.example.com |
Does not work |
Note: This TSM setting is available for Tableau Server only. It is not available for Tableau Cloud.
The most reliable solution for both Tableau Cloud and Tableau Server is to use a reverse proxy. The proxy makes all browser requests—the embedded viz and VDS API calls—appear to originate from a single domain. The proxy routes each request to the correct backend server. Because the browser sees only one domain, the Tableau session cookie set by the embedded viz is automatically included on subsequent VDS calls.
The proxy must route three types of requests:
GET /views/..., including login)POST /node/query)GET /, serving the web app)workgroup_session_id cookie passed from the browser.