The archive sent the document. The browser decided what to do with it.

After an upgrade, documents from OpenText Archive Center download instead of displaying. Nothing is wrong with the archive: one HTTP header decides it, the setting has moved into the Administration Client, and a blank frame is a different problem entirely.

The Monday after an upgrade, the invoices stop appearing. Somebody in accounts payable clicks the attachment on a purchase order the way she has every morning for six years, and instead of the document appearing on the screen a download starts, or a second browser window opens somewhere behind the first. She reports that the archive is broken.

The archive is not broken. It found the document, read it and delivered it, and if you look at the archive's own record of the request you will see it did so in a few dozen milliseconds. Everything the user is complaining about happened after the document had already left the building.

What changed is a single line of text that travels alongside the document and tells the browser what kind of thing it has just received. It is called the Content-Disposition header, it has three possible values, and in recent versions of Archive Center the place where you choose between them has moved. That is the whole story, and it takes about five minutes to fix once you know which of two entirely different problems you are looking at.

Telling the two problems apart

Both problems produce the same sentence from a user, which is that the document did not open. They have nothing else in common, and the first useful act is to decide which one you have.

What the user seesWhat is actually happeningWhere the answer lives
A download starts, or a download bar appearsThe document was labelled as an attachmentContent-Disposition
A second window opens instead of the document appearing in placeThe same label, handled differently by that browserContent-Disposition
The frame is empty, or says the content cannot be displayed in a frameThe browser refused to embed the document at allFrame ancestors and CORS
Nothing loads, and the surrounding page is httpsMixed content, an http document inside an https pageThe URL itself

That last row is worth checking before anything else, because it costs nothing. A browser will always block content loaded over plain http into a page served over https, and no amount of configuration on the archive will change its mind. If the document URL and the page URL do not agree on the protocol, stop reading and fix that.

The rest of this article deals with the first two rows, and then with the third.

What the header actually says

Content-Disposition is a standard HTTP header. It has nothing to do with OpenText specifically. It is the archive's way of answering a question the browser is about to ask: is this something to display, or something to save?

ValueWhat the browser doesWhen you want it
inlineDisplays the document in the page if it can, which for PDF it usually canAlmost always. This is what "showing the invoice" means
attachmentDownloads the file. The user then has to find it and open itWhen the file is meant to be taken away rather than read on screen
noneNo defined behaviour. The application opening the URL decidesRarely, but see below

Most organisations want inline, because inline is what people mean when they say the document should open. none sounds like a broken setting and occasionally is not: some older client software behaves better when the archive stays quiet and lets the calling application make the decision. It is a legitimate answer, but it is a deliberate one, not a default to settle for.

Where the setting lives now

This is the part that catches people after an upgrade. Until version 21.1 the value was a Java option on the Tomcat process, which meant it lived on the server, in a startup parameter, and was found by whoever remembered it was there.

From 21.1 onwards it moved into the Archive Center configuration itself, along with a lot of neighbouring settings. That consolidation is a genuine improvement, because it also allows the value to be set per application: two applications running in the same Tomcat can now be told to behave differently. But it does mean the old Java option is no longer where the answer lives, and an upgrade will quietly stop honouring it.

Archive Center 21.1 and later. In the Administration Client, expand Configuration, then Integration Common Service Settings (ICS), then HTTP communication parameter. Inside it is an entry called Controls appearance of Content-Disposition header. Open its properties, choose the value you want, confirm, and restart the Tomcat and Spawner services.

Archive Center before 21.1, on Windows. Open Configure Tomcat, go to the Java tab, and add the option to the bottom of the Java Options box:

-DContentDispositionType=inline

Apply, confirm, and restart Tomcat and Spawner.

Archive Center before 21.1, on Linux. The same option goes on the CATALINA_OPTS line in setenv.sh, typically at a path like /opentext/tomcat7/bin/setenv.sh. Save, then restart Tomcat and Spawner.

Core Archive Connector. The setting lives in ICS.Setup, under <opentext>\config\setup, as a named parameter rather than a Java option:

# Controls appearance of Content-Disposition header
CONTENT_DISPOSITION_TYPE=attachment

Note the value in that example. attachment is what ships, which means a Core Archive Connector that nobody has configured will download every document it serves, working exactly as designed and satisfying nobody.

In all four cases the application server has to be restarted before anything changes. There is no reload, no refresh, no clearing of a cache that will get you there. Plan the five minute change into a window rather than promising somebody it will be fixed before lunch.

The exception list, which is more useful than it looks

Core Archive Connector carries a second parameter next to the first:

# List of filename extensions
CONTENT_DISPOSITION_EXCEPTION_LIST=

It inverts the main setting for the extensions you name. With CONTENT_DISPOSITION_TYPE=attachment, anything on the list is sent inline instead, and with inline configured, anything on the list is sent as an attachment.

That inversion is the answer to a problem most people solve badly. Not every document type belongs on screen. A PDF invoice does. An archived Outlook message does not, because no browser can render an MSG file and asking it to try produces either a wall of encoded text or nothing at all. Rather than choosing one behaviour for the entire archive and living with whichever half is wrong, set the majority behaviour globally and list the exceptions.

When it is the frame, not the disposition

Now the third row of the first table, which is a different mechanism entirely and produces a blank frame or an explicit refusal rather than a download.

When a web application embeds a document from another server in one of its own frames, SAP Fiori being the common case, the browser treats those as two different origins and blocks the combination by default. This is deliberate. It is the protection against cross-site scripting and clickjacking, and it works whether or not the two servers belong to the same organisation.

Making it work requires the archive to name the calling application as legitimate, in the response it sends back. If a Fiori server asks the archive for a document to place in a frame, the archive's answer has to state that this Fiori server is allowed to display it. Three headers carry that statement:

HeaderWhat it governs
CONTENT_SECURITY_POLICY_FRAME_ANCESTORSWhich pages may embed this content. The one that matters on current browsers
X_FRAME_OPTIONSThe older anti-clickjacking control, still set for compatibility
ACCESS_CONTROL_ALLOW_ORIGINWhich origins may read the response at all

One thing worth knowing before you spend an afternoon on it: the ALLOW-FROM form of X-Frame-Options was never widely implemented and current browsers ignore it. Set it if the documentation tells you to, but do not expect it to be what fixes the problem. The frame ancestors policy is what modern browsers actually read.

Writing the address correctly

The value is a URL mask, and its punctuation is unforgiving in ways that cost people hours.

https://my.fioriserver.my.domain:8443     one server, one port, host only
https://my.fioriserver.my.domain:8443/    the same, but paths included
https://my.fioriserver.my.domain:*/       any port on that server, all paths
https://*.my.domain:8443                  any server in the domain, one port
https://*.my.domain:*/                    any server, any port, any path

Two rules govern all of them. The colon before the port is required and cannot be omitted, even when the port is a wildcard. And a trailing slash is what extends the mask to cover paths: without it only the host is matched, which is the single most common reason a mask that looks correct does nothing.

Several masks can be combined by separating them with spaces:

https://*.my.domain:*/ https://*.other.domain:7654 https://my.server.third.domain:*/

Where the values go

Product and versionLocation
Core Archive ConnectorACS.Setup, using the CUSTOMHEADER_ parameters
Archive Center 23.3 and laterAS.Setup, using the same CUSTOMHEADER_ parameters
Archive Center 23.2 and earlierweb.xml, using the Tomcat CORS and header security filters

For 23.3 and later, and for Core Archive Connector, the two settings are named directly:

CUSTOMHEADER_CONTENT_SECURITY_POLICY_FRAME_ANCESTORS=https://*.my.domain:*/
CUSTOMHEADER_X_FRAME_OPTIONS=ALLOW-FROM https://*.my.domain:*/

On 23.2 and earlier there is no such shortcut, and the same job is done by configuring Tomcat's own filters in web.xml. The CORS filter is the shorter of the two:

<filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
  <init-param>
    <param-name>cors.allowed.origins</param-name>
    <param-value>https://my.fiori.server:453,https://my.own.web.application:3600</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Note that this one takes a comma-separated list rather than a space-separated one. The anti-clickjacking side is handled by HttpHeaderSecurityFilter in the same file, through antiClickJackingEnabled, antiClickJackingOption and antiClickJackingUri. Both are standard Tomcat, documented by the Tomcat project rather than by OpenText, and the version of Tomcat matters more than the version of Archive Center when you go looking.

The archive did its part in fifty milliseconds. Everything the user complained about happened after that.

— one upgrade, four days spent blaming the wrong component

None of this is difficult, and none of it is really about archiving. It is a browser security model that tightened over the last decade meeting a product whose configuration moved to keep up with it, and the collision happens on the Monday after an upgrade because that is when both change at once.

Which makes the useful question a small one. Find out which of the three values your archive is sending today, and find out whether anybody chose it. Very often the answer is that nobody did, and the behaviour your users are describing is simply the default that arrived with the new version, waiting for somebody to notice it was never a decision.

Portrait of Ruud Palmen
Written by

Ruud Palmen — ECM Solution Architect

15+ years of OpenText. More background on the about page, or follow along on LinkedIn.