Onlyoffice integration in Nextcloud - X-Frame-Options trouble

Hi all

I’m running a small home NAS on FreeNAS 11.2 U7. For a long time I’m trying to integrate an Onlyoffice document-server into Nextcloud. Finally, with RancherOS I managed to successfully install an Onlyoffice docker image. The testpage with the green checkmark shows up for port 80 as wells as 443 with a self-signed certificate. The integration into nextcloud works like a charm, when using the local IP. At first I thought I did it, but of course this doesn’t work with remote access.

So, now I’m trying to make my reverse-proxy forward /onlyoffice the same way it also forwards /nextcloud to the corresponding jail/vm. However I keep running in the error message “Unknown error”. Took my some time to find the error, because there was nothing in all of the nginx-logs. Opening the Firefox console finally gave me some insight:

> The select2 library is deprecated! It will be removed in nextcloud 19. 2 globals.js:30:10
Content Security Policy: Ignoring “'unsafe-inline'” within script-src or style-src: nonce-source or hash-source specified
JQMIGRATE: Migrate is installed, version 1.4.1 jquery-migrate.min.js:2:494
Content Security Policy: Ignoring “'unsafe-inline'” within script-src or style-src: nonce-source or hash-source specified 2
Invalid X-Frame-Options: “ALLOW-FROM https://my.domain/” header from “https://my.domain/onlyoffice/5.4.1-39//web-apps/apps….1-39&lang=en&customer=ONLYOFFICE&frameEditorId=iframeEditor” loaded into “https://my.domin/nextcloud/index.php/apps/onlyoffice/408302?filePath=%2Ftemp%2Fkljf.xlsx”.

An unbalanced tree was written using document.write() causing data from the network to be reparsed. For more information index.html:272
wasm streaming compile failed: TypeError: Response has unsupported MIME type fonts.js:14:55
falling back to ArrayBuffer instantiation fonts.js:14:95
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at…&expires=1575280299&disposition=attachment&ooname=output.bin. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

So, somehow my X-frame-options don’t fit. Unfortunately I am no professional and only managed to get this far by following how-tos and a looot of googling issues that arise. So now I assume I need to use some add_header x-frame-options lines with the correct argument in the correct file. But where? in Nextcloud? onlyoffice? the reverse proxy? Or is it the last line in the log, where it tries to access the local IP instead of my.domain causing a SAMEORIGIN infraction? I hope this may be an obvious mistake to some of you and I’d be happy to be pointed in the right direction.

My setup:

  • Nextcloud in iocage jail
    Nextcloud config.php:
$CONFIG = array (
 'instanceid' => '<<redacted>>',
 'passwordsalt' => '<<redacted>>',
 'secret' => '<<redacted>>',
 'trusted_domains' =>
 array (
   0 => '',
   1 => '',
   2 => 'my.domain',
 'trusted_proxies' =>
 array (
   0 => '',
   1 => 'my.domain:80',
 'datadirectory' => '/mnt/data',
 'share_folder' => '/Shared',
 'dbtype' => 'mysql',
 'version' => '',
 'overwriteprotocol' => 'https',
 'overwrite.cli.url' => 'https://my.domain/nextcloud',
 'dbname' => 'nextcloud',
 'dbhost' => 'localhost:/tmp/mysql.sock',
 'dbport' => '',
 'dbtableprefix' => 'oc_',
 'mysql.utf8mb4' => true,
 'dbuser' => '<<redacted>>',
 'dbpassword' => '<<redacted>>',
 'installed' => true,
 'redis' =>
 array (
   'host' => '/tmp/redis.sock',
   'port' => 0,
 'memcache.local' => '\\OC\\Memcache\\APCu',
 'memcache.locking' => '\\OC\\Memcache\\Redis',
 'mail_smtpmode' => 'smtp',
 'mail_smtpsecure' => 'ssl',
 'mail_sendmailmode' => 'smtp',
 'mail_from_address' => '',
 'mail_domain' => '',
 'mail_smtpauthtype' => 'LOGIN',
 'mail_smtpauth' => 1,
 'mail_smtphost' => '',
 'mail_smtpport' => '465',
 'mail_smtpname' => '',
 'mail_smtppassword' => '<<redacted>>',
 'maintenance' => false,
 'onlyoffice' =>
 array (
   'verify_peer_off' => true,
 'theme' => '',
 'loglevel' => 2,

Nextcloud nginx.conf:

load_module /usr/local/libexec/nginx/;
load_module /usr/local/libexec/nginx/;

user www;
worker_processes 8;

pid /var/run/;

events {
  use kqueue;
  worker_connections 1024;
  multi_accept on;
http {

  # Basic settings
  # ----------

  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  reset_timedout_connection on;
  keepalive_timeout 65;
  keepalive_requests 1000;
  types_hash_max_size 2048;
  server_tokens off;
  send_timeout 30;
  server_names_hash_max_size 4096;

  # Common limits
  # ----------

  client_max_body_size 16G; # upload size
  client_body_buffer_size 1m;
  client_header_timeout 3m;
  client_body_timeout 3m;

  client_body_temp_path /var/tmp/nginx/client_body_temp;

  proxy_connect_timeout 5;
  proxy_send_timeout 10;
  proxy_read_timeout 10;

  proxy_buffer_size 4k;
  proxy_buffers 8 16k;
  proxy_busy_buffers_size 64k;
  proxy_temp_file_write_size 64k;
#  proxy_max_temp_file_size 0;

  proxy_temp_path /var/tmp/nginx/proxy_temp;

  include mime.types;
  default_type application/octet-stream;

  # Logs format
  # ----------

  log_format main '$remote_addr - $host [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"'
                  'rt=$request_time ut=$upstream_response_time '
  log_format cache '$remote_addr - $host [$time_local] "$request" $status '
                   '$body_bytes_sent "$http_referer" '
                   'rt=$request_time ut=$upstream_response_time '

  access_log /var/log/nginx/access.log main;
  error_log /var/log/nginx/error.log warn;

  # GZip config
  # ----------

  gzip on;
  gzip_static on;
  gzip_types text/plain text/css text/javascript text/xml application/x-javascript application/jav$
  gzip_comp_level 9;
  gzip_buffers 16 8k;
  gzip_proxied expired no-cache no-store private auth;
  gzip_min_length 1000;
  gzip_disable "msie6"
  gzip_vary on;

  # Cache config
  # ----------

  proxy_cache_valid 1m;

  # Virtual host config
  # ----------

  include /usr/local/etc/nginx/conf.d/*.conf;

Nextcloud nginx include: nextcloud.conf

server {
  listen 80;
  server_name my.domain;

  # Path to the root of your installation
  root /usr/local/www;

  # set max upload size
  client_max_body_size 10G;
  fastcgi_buffers 64 4K;

  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

  # Disable gzip to avoid the removal of the ETag header
  gzip off;

#  rewrite ^/caldav(.*)$ /remote.php/caldav$1 redirect;
#  rewrite ^/carddav(.*)$ /remote.php/carddav$1 redirect;
#  rewrite ^/webdav(.*)$ /remote.php/webdav$1 redirect;

#  location = /.well-known/carddav {
#    return 301 $scheme://$host/remote.php/dav;
#  }
#  location = /.well-known/caldav {
#    return 301 $scheme://$host/remote.php/dav;
#  }

  index index.php;
  error_page 403 /core/templates/403.php;
  error_page 404 /core/templates/404.php;

  location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;

  location ~ ^/(?:\.htaccess|data|config|db_structure\.xml|README){
    deny all;

  location / {
    # The following 2 rules are only needed with webfinger
    rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
    rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;

    rewrite ^/.well-known/carddav /remote.php/carddav/ redirect;
    rewrite ^/.well-known/caldav /remote.php/caldav/ redirect;

    rewrite ^(/core/doc/[^\/]+/)$ $1/index.html;

    try_files $uri $uri/ =404;

  location ~ \.php(?:$|/) {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_pass unix:/var/run/php-fpm.sock;
    fastcgi_intercept_errors on;

  # Adding the cache control header for js and css files
  # Make sure it is BELOW the location ~ \.php(?:$|/) { block
  location ~* \.(?:css|js)$ {
    add_header Cache-Control "public, max-age=7200";
    # Add headers to serve security related headers
    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag none;
    # Optional: Don't log access to assets
    access_log off;

  # Optional: Don't log access to other assets
  location ~* \.(?:jpg|jpeg|gif|bmp|ico|png|swf)$ {
    access_log off;
  • Onlyoffice Docker in RancherOS with self-signed certificate:
    Onlyoffice nginx include ds.conf:
include /etc/nginx/includes/http-common.conf;

## Normal HTTP host
server {
  listen [::]:80 default_server;
  server_name _;
  server_tokens off;

  ## Redirects all traffic to the HTTPS host
  root /nowhere; ## root doesn't have to be a valid path since we are redirecting
  rewrite ^ https://$host$request_uri? permanent;

#HTTP host for internal services
server {
  listen [::1]:80;
#  server_name localhost;
  server_name my.domain;
  server_tokens off;

  include /etc/nginx/includes/ds-common.conf;
  include /etc/nginx/includes/ds-docservice.conf;

## HTTPS host
server {
  listen ssl http2;
  listen [::]:443 ssl http2 default_server;
  server_tokens off;
  root /usr/share/nginx/html;

  ## Strong SSL Security
  ssl on;
  ssl_certificate <<redacted>>;
  ssl_certificate_key <<redacted>>;
  ssl_verify_client off;


  ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
  ssl_session_cache  builtin:1000  shared:SSL:10m;

  ssl_prefer_server_ciphers   on;

  add_header Strict-Transport-Security max-age=31536000;
  # add_header X-Frame-Options SAMEORIGIN;
  add_header X-Content-Type-Options nosniff;

  ## [Optional] If your certficate has OCSP, enable OCSP stapling to reduce the overhead and latency o$
  ## Replace with your ssl_trusted_certificate. For more info see:
  ## -
  ## -
  ## -$
  # ssl_stapling on;
  # ssl_stapling_verify on;
  # ssl_trusted_certificate /etc/nginx/ssl/stapling.trusted.crt;
  # resolver valid=300s; # Can change to your DNS resolver if desired
  # resolver_timeout 10s;

  ## [Optional] Generate a stronger DHE parameter:
  ##   cd /etc/ssl/certs
  ##   sudo openssl dhparam -out dhparam.pem 4096
  ssl_dhparam <<redacted>>;

  include /etc/nginx/includes/ds-*.conf;

  • Nginx reverse-proxy. This redirects incoming requests to the correct IP and handles my LetsEncrypt certificate for all my jails.
    rev-proxy nginx.conf:
load_module /usr/local/libexec/nginx/;
load_module /usr/local/libexec/nginx/;

#user  nobody;
worker_processes  6;

# This default error log path is compiled-in to make sure configuration parsing
# errors are logged somewhere, especially during unattended boot when stderr
# isn't normally logged anywhere. This path will be touched on every nginx
# start regardless of error log location configured here. See
# for more info.
#error_log  /var/log/nginx/error.log;

#pid        logs/;

events {
    worker_connections  1024;

http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;
    proxy_connect_timeout  7200s;
    proxy_send_timeout     7200s;
    proxy_read_timeout     7200s;
    proxy_buffering          off;
#    proxy_max_temp_file_size 16G;
    send_timeout           7200s;

    #gzip  on;

#------- Plex stuff-------------
    upstream plex {
    keepalive 32;

#---------Onlyoffice stuff-----------
    upstream docservice {


   map $http_host $this_host {
       "" $host;
       default $http_host;

   map $http_x_forwarded_proto $the_scheme {
       default $http_x_forwarded_proto;
       "" $scheme;

   map $http_x_forwarded_host $the_host {
      default $http_x_forwarded_host;
      "" $this_host;

   map $http_upgrade $connection_upgrade {
      default upgrade;
      ''      close;

#-----standard stuf-------------------
#    map $http_x_forwarded_proto $proxy_x_forwarded_proto {
#      default $http_x_forwarded_proto;
#      ''      $scheme;
#    }

# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
# Connection header that may have been passed to this server
    map $http_upgrade $proxy_connection {
      default upgrade;
      '' close;

    # listen on port 80 -> redirect to 443
    server {
     listen 80;
         return 301 https://$host$request_uri;

    # listen on port 443 primarily
    server {
        listen 443 ssl;
        server_name my.domain;
        include ssl_common.conf;
        include proxy_setup.conf;

        client_max_body_size 16G;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

# ------- Plex stuff ------------
        location / {
            root   /usr/local/www/nginx;
            index  index.html index.htm;

            if ($args ~ (.*)X-Plex-Device(.*)) {
                proxy_pass http://plex;

            if ($http_referer ~ (.*)plex(.*)) {
                proxy_pass http://plex;

        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/local/www/nginx-dist;

rev-proxy ssl_common.conf:

# Thanks to for providing a great reference! Please check out their site
# to make sure your SSL Configuration is up to date with current standards! Be aware that in this
# example we use a slightly liberal cipherlist to allow for older browsers on older devices, Eg.
# IE8, android 2.4, etc

# Enable Perfect Forward Secrecy (PFS)
ssl_prefer_server_ciphers on;
ssl_certificate <<redacted>>
ssl_certificate_key <<redacted>>

# Disable SSLv2 and SSLv3 (BEAST and POODLE attacks)
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_protocols TLSv1.3 TLSv1.2;

# Enable our strong DH Key
ssl_dhparam <<redacted>>

# Cipher-list for PFS.
ssl_ecdh_curve secp384r1;

# Requires nginx >= 1.1.0
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_session_timeout 10m;

# Requires nginx >= 1.5.9
ssl_stapling on;

# Requires nginx >= 1.3.7
ssl_stapling_verify on;

# Requires nginx => 1.3.7
resolver valid=7200s;
resolver_timeout 5s;

# HSTS Support
add_header Strict-Transport-Security "max-age=63072000;includeSubdomains; 

# These headers can break applications, be careful!
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;

rev-proxy proxy_setup.conf

location /nextcloud {
     proxy_redirect https://my.domain/nextcloud;

     proxy_headers_hash_max_size 512;
     proxy_headers_hash_bucket_size 64;

     proxy_set_header Host $host;
     proxy_set_header X-Forwarded-Proto $scheme;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     add_header Front-End-Https on;

location  /onlyoffice {
        return 302 /onlyoffice/;
location /onlyoffice/ {
        proxy_redirect https://my.domain/onlyoffice;

        proxy_read_timeout 1800;
        proxy_connect_timeout 1800;

#    proxy_http_version 1.1;
#        add_header Strict-Transport-Security max-age=31536000;

        proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header X-Forwarded-Proto $scheme;

#        proxy_set_header Connection $proxy_connection;
#        proxy_set_header X-Real-IP $remote_addr;
#        proxy_set_header X-Forwarded-Host $the_host;
#        proxy_set_header X-Forwarded-Proto $the_scheme;
#        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        add_header X-Frame-Options "ALLOW-FROM https://my.domain/nextcloud" always;
#        add_header X-Frame-Options sameorigin always;
#        proxy_hide_header 'X-Frame-Options'; 
#        add_header Font-End-Https on;
#        add_header Access-Control-Allow-Origin "https://my.domain/nextcloud";


        location ~* ^/ds-vpath/ {
            rewrite /ds-vpath/(.*) /$1 break;
            proxy_redirect off;

            client_max_body_size 100m;

            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";

            proxy_set_header Host $http_host;
            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 $the_host/ds-vpath;
            proxy_set_header X-Forwarded-Proto $the_scheme;

location /transmission {
     proxy_redirect off;
     proxy_set_header Host $host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#     proxy_pass_header X-Transmission-Session-Id;
     add_header   Front-End-Https   on;

location /sonarr {
     proxy_set_header Host $host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     add_header Front-End-Https on;

     proxy_redirect off;
     proxy_buffering off;

location /radarr {
     proxy_set_header Host $host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     add_header Front-End-Https on;
     proxy_redirect off;
     proxy_buffering off;

# plex IP defined in nginx.conf (upstream plex {...}
location ~ ^/(\?(?:.*)(X-Plex-Device=)|web|video|photo|library|web|status|system|updater|clients|:|playQueues)(.*){
    proxy_pass http://plex;
    proxy_redirect  http://plex /;
    # set some headers and proxy stuff.
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_redirect off;

    # include Host header
    proxy_set_header Host $host;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 36000s;
    proxy_pass_request_headers on;

location /plex {
    error_log /var/log/nginx/plex.error.log debug;
    rewrite_log on;
    rewrite ^/plex(.*)$ /web$1 break;
    proxy_pass http://plex;
    proxy_headers_hash_max_size 51200;
    proxy_headers_hash_bucket_size 6400;
    proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 36000s;
    proxy_pass_request_headers on;

I redacted all sensitive data, and replaced my domain name with my.domain. If you spot anything else, that I shouldn’t be sharing openly, please tell me : ) Same goes, if some important information is missing.

Thank you guys very much in advance.


Anybody? Even a small push in the right direction is appreciated…

may be it’s helpful.

this is my nginx.conf. it’s a jinja2 template. hope you can read it.

in my setup nginx is the webserver in front of nextcloud fpm-php. i only configure /ds-vpath/ to access onlyoffice.

the headers added by traefik (the ingress router) you’ll find here.

if you are using selfsigned certificates that won’t work yet. the following commit has to be merge to the onlyoffice docker. till then you have edit an xml file in the container manually.