Proxying Tableau Server Content Using PHP and Trusted Tickets


Proxying Tableau Server Content Using PHP and Trusted Tickets

by Josh Varner

It’s common for Tableau Server users to want to embed content into a separate web application while maintaining tight security restrictions. If you want your web application to handle all of the authentication, it’s possible to use the trusted ticket authentication system offered by Tableau Server. It’s important, however, to understand the request workflow that Tableau Server will use. In this example, we’ll build a simple PHP script that will proxy Tableau Server content using the trusted ticket authentication system.

It’s important to note that this example script does not perform any authentication. When building a production version of this script, you’ll want to be sure that the user is authenticated properly. Tableau Server, in this case, is simply trusting the web app server running the script to perform whatever authentication is necessary.

Before we begin, you’ll want to make sure of the following:

  1. The web app server you’ll be running your PHP script on needs to be “trusted” by Tableau Server. This is done by whitelisting your web app server’s IP address using the following tabadmin commands. Note: this will restart Tableau Server.
    tabadmin set wgserver.trusted_hosts ","
    tabadmin restart
  2. Ensure that the cURL PHP extension is installed and enabled on your web app server. We’ll be using cURL in this example script so that we can properly handle cookies and redirects.

Now that you’ve configured Tableau Server, let’s walk through the request process. Let’s say we’d like to retrieve “http://tbsrv/views/foo/bar?format=png” as the user “jvarner”. Here are the steps we will follow: 

  1. App requests trusted ticket from Tableau Server:
    • POST (username=jvarner, client_ip= http://tbsrv/trusted
    • Server returns integer ticket ID (or negative number in case of error)
  2. App requests content (normally at /views/foo/bar?format=png):
    • GET http://tbsrv/trusted/{$ticketId}/views/foo/bar?format=png
    • Server sets “workgroup_session_id” cookie and redirects to final URL (full response source below for reference)
      HTTP/1.1 302 Moved Temporarily
      Date: Thu, 12 Jul 2012 23:05:40 GMT
      Server: Apache-Coyote/1.1
      Pragma: no-cache
      Expires: Thu, 01 Jan 1970 00:00:00 GMT
      Cache-Control: no-cache
      Cache-Control: no-store
      Location: http://tbsrv/views/foo/bar?format=png
      Content-Length: 0
      Set-Cookie: workgroup_session_id=abkcowijQ1eMeh59jdkvP3mlyJMvYZoE; Path=/; Expires=Fri, 13-Jul-2012 3:5:40 GMT; HttpOnly
      P3P: CP="NON"
      Content-Type: text/plain
  3. App follows redirect, passing the cookie that was set, and fetches content:
    • GET http://tbsrv/views/foo/bar?format=png
    • Server returns the PNG image

Below is our example PHP script, with the workflow annotated within the code’s comments. To use this, you’ll want to change the variables at the top, add/integrate authentication, and add any additional error handling as necessary.

The code below has also been posted as a GitHub Gist. If you have any questions or comments about the code, please visit the GitHub Gist. If you’d like to read more about trusted tickets, please visit this Tableau Server knowledgebase article.

$server = 'tbsrv';
$username = 'jvarner';
$clientIp = (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '');

// To get a PNG
$url = 'views/foo/bar?format=png';
$contentType = 'image/png';

// To get a PDF you could use:
// $url = 'views/foo/bar?format=pdf';
// $contentType = 'application/pdf';

// Request the trusted ticket ID by sending username & client_ip via POST fields to "/trusted"
$params = compact('username');

if ($clientIp) {
    $params['client_ip'] = $clientIp;

$ch = curl_init();
curl_setopt_array($ch, array(
    CURLOPT_URL            => "http://{$server}/trusted",
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $params,

$ticket = (int) curl_exec($ch);

// Valid ticket IDs are positive integers
if ($ticket  $fullUrl,
    // Tell cURL to follow the redirect from Tableau Server
    // Enable cookies so that cURL handles the session ID cookie, but don't load any existing
    // cookies from a file (since we don't have any)
    CURLOPT_HEADER         => false,

// Simply output the content to the browser
header("Content-Type: {$contentType}");

More About the Author

Josh Varner

Database Engineer
Proper Care and Feeding of Vertica (Part One) (This is a multi-part series about the proper care & feeding of a Vertica cluster. There’s simply too much content for one post, ...
Big Changes in Vertica 6.1 SP2 (6.1.2) About a month ago, HP Vertica released its latest service pack, HP Vertica 6.1 Service Pack 2 (version 6.1.2). It may sound like a ...

See more from this author →

InterWorks uses cookies to allow us to better understand how the site is used. By continuing to use this site, you consent to this policy. Review Policy OK


Interworks GmbH
Ratinger Straße 9
40213 Düsseldorf
Geschäftsführer: Mel Stephenson

Telefon: +49 (0)211 5408 5301

Amtsgericht Düsseldorf HRB 79752
UstldNr: DE 313 353 072


Love our blog? You should see our emails. Sign up for our newsletter!