CSRF issue due to Nginx and Apache proxy -> Nginx Vhost config

Hi there,

this issue has taken much of my time, so I decided to try to reach help here in the official nextcloud forum.

I have an nginx reverse proxy and behind there is an apache. It seems like there is a problem with CSRF. In advance: I am an advanced beginner in webserver configuration.

The failure is, that paths to https://cloud.mydomain.com/index.php/apps/notes/notes gets a 502 or 500 http status code and if I open the url directly there It says “Access denied. CSRF check failed”.

I have read many websites to that topic and nginx needs to forward http header informations to the apache. So I made some changes to the vhost config. Below there it is:

server {
  listen 80;
  listen 443 ssl http2;
  ssl_certificate /usr/local/nginx/conf/ssl/cloud.mydomain.com.crt;
  ssl_certificate_key /usr/local/nginx/conf/ssl/cloud.mydomain.com.key;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_session_timeout 10m;
  ssl_session_cache builtin:1000 shared:SSL:10m;
  ssl_buffer_size 1400;
  add_header Strict-Transport-Security max-age=15768000;
  ssl_stapling on;
  ssl_stapling_verify on;
  server_name cloud.bitpage.de;
  access_log /data/wwwlogs/cloud.mydomain.com_nginx.log combined;
  index index.html index.htm index.php;
  root /data/wwwroot/cloud.mydomain.com;
  if ($ssl_protocol = "") { return 301 https://$server_name$request_uri; }
    # set max upload size
    client_max_body_size 512M;
    fastcgi_buffers 64 4K;

    # Disable gzip to avoid the removal of the ETag header
    gzip off;
  location / {
        rewrite ^ /index.php$uri;

    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
        deny all;
    location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
        deny all;

    location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+|core/templates/40[34])\.php(?:$|/) {
      proxy_connect_timeout 300s;
      proxy_send_timeout 900;
      proxy_read_timeout 900;
      proxy_buffer_size 32k;
      proxy_buffers 4 64k;
      proxy_busy_buffers_size 128k;
      proxy_redirect off;
      proxy_hide_header Vary;
      proxy_set_header Accept-Encoding '';
      proxy_set_header Referer $http_referer;
      proxy_set_header Cookie $http_cookie;
      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_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
        proxy_request_buffering off;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass_header Set-Cookie;
        proxy_pass_header X-CSRF-TOKEN;

    location ~ ^/(?:updater|ocs-provider)(?:$|/) {
        try_files $uri/ =404;
        index index.php;
    location = /.well-known/carddav {
      return 301 $scheme://$host/remote.php/dav;
    location = /.well-known/caldav {
      return 301 $scheme://$host/remote.php/dav;

    # Adding the cache control header for js and css files
    # Make sure it is BELOW the PHP block
    location ~* \.(?:css|js|woff|svg|gif)$ {
        try_files $uri /index.php$uri$is_args$args;
        add_header Cache-Control "public, max-age=7200";
        # Add headers to serve security related headers (It is intended to
        # have those duplicated to the ones above)
        # Before enabling Strict-Transport-Security headers please read into
        # this topic first.
        # add_header Strict-Transport-Security "max-age=15768000;
        #  includeSubDomains; preload;";
        # WARNING: Only add the preload option once you read about
        # the consequences in https://hstspreload.org/. This option
        # will add the domain to a hardcoded list that is shipped
        # in all major browsers and getting removed from this list
        # could take several months.
        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;
        add_header X-Download-Options noopen;
        add_header X-Permitted-Cross-Domain-Policies none;
        # Optional: Don't log access to assets
        access_log off;

    location ~* \.(?:png|html|ttf|ico|jpg|jpeg)$ {
        try_files $uri /index.php$uri$is_args$args;
        # Optional: Don't log access to other assets
        access_log off;

The corresponding apache vhost:
<VirtualHost *:88>
ServerAdmin admin@example.com
DocumentRoot "/data/wwwroot/cloud.mydomain.com"
ServerName cloud.mydomain.com

  SSLEngine on
  SSLCertificateFile "/usr/local/nginx/conf/ssl/cloud.mydomain.com.crt"
  SSLCertificateKeyFile "/usr/local/nginx/conf/ssl/cloud.mydomain.com.key"
  ErrorLog "/data/wwwlogs/cloud.mydomain.com_error_apache.log"
  CustomLog "/data/wwwlogs/cloud.mydomain.com_apache.log" common
<Directory "/data/wwwroot/cloud.mydomain.com">
  SetOutputFilter DEFLATE
  Options FollowSymLinks ExecCGI
  Require all granted
  AllowOverride All
  Order allow,deny
  Allow from all
  DirectoryIndex index.html index.php

In the apache access log there I found an entry: 88.76.xxx.xx - - [28/May/2017:21:12:43 +0800] "GET /index.php/apps/notes/notes HTTP/1.0" 500 16889

So it seems like my correct user IP is relayed because the server ID starts with 81.xxx

my config/config.php looks like this:

$CONFIG = array (
  'instanceid' => 'ocxxxpxwapx6',
  'passwordsalt' => '2xs8+fNe6JTfD//gR51zc9whIBiRBX',
  'secret' => 'I+Iej2gq8k5u5ZFuMkWoherhgwL+4ohBYnqT21lpQVfntXul',
  'trusted_domains' => 
  array (
    0 => 'cloud.mydomain.com',
    1 => '',
    2 => 'localhost',
    3 => 'localhost:88',
    4 => '',
  'datadirectory' => '/data/wwwroot/nextcloud-data',
  'trusted_proxies'   => [''], 
  'overwrite.cli.url' => 'https://cloud.mydomain.com',
  'overwritehost' => 'cloud.mydomain.com',
  'overwriteprotocol' => 'https',  
  'enable_avatars' => true,
  'dbtype' => 'mysql',
  'version' => '',
  'dbname' => 'nextcloud',
  'dbhost' => 'localhost',
  'dbport' => '',
  'dbtableprefix' => 'oc_',
  'dbuser' => 'nextcloud',
  'dbpassword' => 'removed',
  'logtimezone' => 'UTC',
  'installed' => true,
  'mail_smtpmode' => 'php',
  'mail_smtpsecure' => 'ssl',
  'mail_from_address' => 'cloud-mydomain',
  'mail_domain' => 'myhoster.com',
  'mail_smtpauthtype' => 'LOGIN',
  'mail_smtpauth' => 1,
  'mail_smtpname' => 'mail@mydomain.com',
  'mail_smtppassword' => 'removed',
  'mail_smtphost' => 'mail.mydomain.com',
  'maintenance' => false,
  'updater.secret' => '$2y$100$KAs7brOtj6uldSak5miUjeq16CfISLPQBtw3ePhiEJ1RpHWAw6BjW',
  'theme' => '',
  'loglevel' => 2,

Hint: I have anonymized the output here… but the configuration is left :wink:

Do you have an idea where could be the failure?

I don’t think it will rely on that, but there is also a php failure that the extension sysvmsg.so cannot be found, but i think that is another topic…