• If you are still using CentOS 7.9, it's time to convert to Alma 8 with the free centos2alma tool by Plesk or Plesk Migrator. Please let us know your experiences or concerns in this thread:
    CentOS2Alma discussion

Question Specification of X header such as X-Frame-Options

Zoo3

Regular Pleskian
I use Nextcloud. Even though I do not specify an X header*, when I search the header with a web browser development tool such as Firefox, the X header is already specified.
*X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, X-Download-Options, X-Permitted-Cross-Domain-Policies

Does Plesk specify an X header as an initial value?
I can access them in your first place, my first web browser and see them.

Settings related to Nextcloud's X header are strange.
Nextcloud has basic configuration validation, but I pass validation even though I have not configured them. If I specify them in nginx.conf or Plesk's nignx directive, validation will fail.

---
Plesk 17.8.11, PHP 7.3.5, Nginx 1.14.2
 
If I check on that site without writing an X header, the X header is not applied. Result is also same by curl -I command. The X header is not applied on the top page of my Nextcloud site.

BUT when I verify such as curl or web browser develper tool in (ex)HTTPS://MYSITE.COM/index.php or internal file, X header is applied. So I think that Nextcloud's own verification passes it. Will Nextcloud have the ability to automatically generate X headers? I do not think that. Is the server unable to release the cache? In my Plesk I do not use Apache but use Nginx, and I disable nginx caching. I have not described X header at all in nginx.conf or nginx directive column on Plesk.
 
Last edited:
Plesk is not setting these headers and has no influence on them. These headers are set by Nextcloud itself via the .htaccess file provided. The only header you will not find in the .htaccess file is the "X-Frame-OptionsSAMEORIGIN" since this is being hardcoded by Nextcloud for some stupid reason.
 
Plesk is not setting these headers and has no influence on them. These headers are set by Nextcloud itself via the .htaccess file provided. The only header you will not find in the .htaccess file is the "X-Frame-OptionsSAMEORIGIN" since this is being hardcoded by Nextcloud for some stupid reason.

I can no longer think of it as Nextcloud gives. There is no description of X-Frame-Options in Nextcloud's .htaccess file, but there are others.
I select any file in Chrome and Firefox development tools and check the header. Then strangely all X headers are doubled. Even with X-Frames-Options. I have not written X-Frame-Options in Nginx or Nextcloud's .htaccess file either. If Nextcloud is adding, it should be one.
When I create a subdomain and access it, X headers are not attached. After all there is something in Nextcloud.

It's a fundamental question. I am using Nginx. Is Nextcloud's .htaccess used? Even though I removed X headers from Nextcloud's .htaccess, those headers have been added.
 
As I said in my previous post, the X-Frame-Options header is set by Nextcloud automatically as in, it being hardcoded in a php file. The others in the .htaccess file are

Code:
<IfModule mod_env.c>
    # Add security and privacy related headers
    Header set X-Content-Type-Options "nosniff"
    Header set X-XSS-Protection "1; mode=block"
    Header set X-Robots-Tag "none"
    Header set X-Download-Options "noopen"
    Header set X-Permitted-Cross-Domain-Policies "none"
    Header set Referrer-Policy "no-referrer"
    SetEnv modHeadersAvailable true
  </IfModule>

As for the double Headers in your browsers development tools, can you go to securityheaders.com and enter the domain to your nextcloud and scan it? See if the website tells you the same thing.

I agree, if you have not set the headers yourself via nginx or Apache, there should only be one header of each. Please could you check the global and your domain nginx config again, just to make sure there are no headers set in these? Please also check the apache config, if they set headers for some reason.

Best practice with nginx is, not to use a .htaccess file and add everything from the .htaccess to your nginx config. This is however not always possible, since many software creators, like Nextcloud, always include this file and kind of make you rely on it. I had a similar issue with this combination, as I had everything setup in my nginx and Nextcloud kept telling me, that I am missing the headers but I wasn't. So I checked in the development tools of the browser and double checked on the website securityheaders.com and found out, that headers were being sent twice. I commented all the "Header set" in the .htaccess file and that did the trick.
 
Last edited:
can you go to securityheaders.com and enter the domain to your nextcloud and scan it? See if the website tells you the same thing.

1. If I don't write X headers in Nginx.
*Pass the NC verification function.

Enter mynextcloud.com:
Rank is D. (Set HSTS only).

Enter mynextcloud.com/index.php
Rank is A+ (Set other than Feature-Policy. And CSP is also automatically assigned.)

2. If I write X headers in Nginx.
*A warnings are issued if I set the X headers in the verification function of NC.

Code:
add_header X-Content-Type-Options nosniff;
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;
add_header Referrer-Policy no-referrer;
add_header X-Frame-Options SAMEORIGIN;

Enter mynextcloud.com:
Rank is A. (Set other than CSP and Feature-Policy).
There are no duplicate headers.

Enter mynextcloud.com/index.php
Rank is A+ (Set other than Feature-Policy. And CSP is also automatically assigned.)
Every X headers are duplicated.
 
By "verification function of NC" you mean the Overview page under Settings of the "admin" user? To be honest I kind of find this information misleading and/or way too harsh. You are the admin and you should decide what you need, not the software!

I would suggest to comment out all the Header sets in the .htaccess of NC and put them in your nginx config instead, apart from the "X-Frame-Options SAMEORIGIN;" one, since this is already sent by NC automatically (hardcoded as mentioned above).

Regarding the Feature-Policy, you can check this for the future but it's not necessarily important just yet. And before you introduce this header, I would suggest to create a concept of what is needed and what not, as this could mess with the functionality of your NC.
 
I edited Nextcloud's .htaccess and commented out everything. But there is no change when everything is duplicated.
I have disabled "proxy mode" in the Plesk nginx configuration. Is the .htaccess file of NC read?
What I am concerned about is that a warning has been issued in duplicate. If NC says it warns, will X headers be invalidated if duplicates occur?
 
Please could you post the content of your NC .htaccess and your apache/nginx config file? (You can hide any server related information such as IP/Hostnames)

Even if you comment out the Header sets in the .htaccess file, the file will still be read by the web server because the file is present. It should however not set Headers, if you have commented them out in that file. In this case only the Apache/Nginx can still set Headers, if setup.
 
Nextcloud's .htaccess *The content is unedited and just commented out.
Code:
<IfModule mod_headers.c>
#  <IfModule mod_setenvif.c>
#    <IfModule mod_fcgid.c>
#       SetEnvIfNoCase ^Authorization$ "(.+)" XAUTHORIZATION=$1
#       RequestHeader set XAuthorization %{XAUTHORIZATION}e env=XAUTHORIZATION
#    </IfModule>
#    <IfModule mod_proxy_fcgi.c>
#       SetEnvIfNoCase Authorization "(.+)" HTTP_AUTHORIZATION=$1
#    </IfModule>
#  </IfModule>

#  <IfModule mod_env.c>
   # Add security and privacy related headers
#    Header set X-Content-Type-Options "nosniff"
#    Header set X-XSS-Protection "1; mode=block"
#    Header set X-Robots-Tag "none"
#    Header set X-Download-Options "noopen"
#    Header set X-Permitted-Cross-Domain-Policies "none"
#    Header set Referrer-Policy "no-referrer"
#    SetEnv modHeadersAvailable true
#  </IfModule>

  # Add cache control for static resources
#  <FilesMatch "\.(css|js|svg|gif)$">
#    Header set Cache-Control "max-age=15778463"
#  </FilesMatch>

  # Let browsers cache WOFF files for a week
#  <FilesMatch "\.woff2?$">
#    Header set Cache-Control "max-age=604800"
#  </FilesMatch>
#</IfModule>
#<IfModule mod_php7.c>
#  php_value mbstring.func_overload 0
#  php_value default_charset 'UTF-8'
#  php_value output_buffering 0
#  <IfModule mod_env.c>
#    SetEnv htaccessWorking true
#  </IfModule>
#</IfModule>
#<IfModule mod_rewrite.c>
#  RewriteEngine on
#  RewriteCond %{HTTP_USER_AGENT}  DavClnt
#  RewriteRule ^$         /remote.php/webdav/          [L,R=302]
#  RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
#  RewriteRule ^\.well-known/host-meta /public.php?service=host-meta [QSA,L]
#  RewriteRule ^\.well-known/host-meta\.json /public.php?service=host-meta-json [QSA,L]
#  RewriteRule ^\.well-known/webfinger /public.php?service=webfinger [QSA,L]
#  RewriteRule ^\.well-known/carddav /remote.php/dav/ [R=301,L]
#  RewriteRule ^\.well-known/caldav /remote.php/dav/ [R=301,L]
#  RewriteRule ^remote/(.*) remote.php [QSA,L]
#  RewriteRule ^(?:build|tests|config|lib|3rdparty|templates)/.* - [R=404,L]
#  RewriteRule ^(?:\.|autotest|occ|issue|indie|db_|console).* - [R=404,L]
#</IfModule>
#<IfModule mod_mime.c>
#  AddType image/svg+xml svg svgz
#  AddEncoding gzip svgz
#</IfModule>
#<IfModule mod_dir.c>
#  DirectoryIndex index.php index.html
#</IfModule>
#AddDefaultCharset utf-8
#Options -Indexes
#<IfModule pagespeed_module>
#  ModPagespeed Off
#</IfModule>
#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####

ErrorDocument 403 /
ErrorDocument 404 /

nginx for Nextcloud *Basically as described in NC.
Code:
# Add headers to serve security related headers
# Before enabling Strict-Transport-Security headers please read into this
# topic first.
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
ssl_protocols TLSv1.2;

# 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-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;
add_header Referrer-Policy no-referrer;
add_header X-Frame-Options SAMEORIGIN;

# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;

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

# The following 2 rules are only needed for the user_webfinger app.
# Uncomment it if you're planning to use this app.
#rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
#rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;

# The following rule is only needed for the Social app.
# Uncomment it if you're planning to use this app.
# rewrite ^/.well-known/webfinger /public.php?service=webfinger last;

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

# set max upload size
client_max_body_size 250M;
fastcgi_buffers 64 4K;
fastcgi_busy_buffers_size 8K;

# Enable gzip but do not remove ETag headers
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

# Uncomment if your server is build with the ngx_pagespeed module
# This module is currently not supported.
#pagespeed off;

location / {
   rewrite ^ /index.php$request_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/.+)\.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_param HTTPS on;
   #Avoid sending the security headers twice
   fastcgi_param modHeadersAvailable true;
   fastcgi_param front_controller_active true;
   fastcgi_pass php-handler;
   fastcgi_intercept_errors on;
   fastcgi_request_buffering off;
}

location ~ ^/(?:updater|ocs-provider)(?:$|/) {
   try_files $uri/ =404;
   index index.php;
}

# Adding the cache control header for js and css files
# Make sure it is BELOW the PHP block
location ~ \.(?:css|js|woff2?|svg|gif)$ {
   try_files $uri /index.php$request_uri;
   add_header Cache-Control "public, max-age=15778463";
   # 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-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;
   add_header Referrer-Policy no-referrer;

   # Optional: Don't log access to assets
   access_log off;

}

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

In the above condition, MYNEXTCLOUD.COM(not MYNEXTCLOUD.COM/index.php) applies X headers one by one. Warnings are displayed on NC. When I see the header information in the browser's development tool after login, there are two duplicate X headers for every item. I understand that CSS or js etc. may be duplicated. PHP files are also duplicated.
I do not get an error even if I intentionally input a typo in the .htaccess file.

Thanks,
 
Either I am just looking wrong or I am noticing, that you are referencing the Headers twice in your Nginx config.

1. Right at the top of your config file

Code:
# 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-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;
add_header Referrer-Policy no-referrer;
add_header X-Frame-Options SAMEORIGIN;

2. Right at the end of your config file

Code:
# Adding the cache control header for js and css files
# Make sure it is BELOW the PHP block
location ~ \.(?:css|js|woff2?|svg|gif)$ {
   try_files $uri /index.php$request_uri;
   add_header Cache-Control "public, max-age=15778463";
   # 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-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;
   add_header Referrer-Policy no-referrer;

This could cause some of the issues you are having. Try removing either 1 or 2, reloading your apache/nginx and revisiting your NC website. (Me personally I would remove the one at the end of the config file)


//EDIT:

Please also remove the header
Code:
add_header X-Frame-Options SAMEORIGIN;
as this will cause a false/positive with the NC checkup, telling you that the Header is not set. The NC checkup is not really accurate, so it will also tell you that Headers are not set, if they've been set twice.

The X-Frame-Options Header is being set by NC in a PHP file and does not need to be set manually by the user.
 
The trouble with this problem is that it's not clear where the problem is.

If I remove the X header from nginx, the NC alert disappears and of course there are no duplicates. When I remove X headers from nginx, validation with an external verification site such as securityheaders.com will not attached the X headers and will be rated low.
*Duplicate when I leave the first X header group.

Should I not write X headers on nginx?
Is there any problem even if the X header are not applied in the top hierarchy of the site because it automatically redirects to index.php? And it's not clear why the X headers are attached. I'm worried that I can't have much confidence in this point.

Do not write X header in nginx:
mynextcloud.com/ --> rate D
mynextcloud.com/index.php --> rate A+​
 
You should put the Headers in Nginx, since this is your websites door to the outside/inside. You want to make sure that everything going out and coming in, is in order.

Please make a backup of your current Nginx config file and put the following in your current config file:

Code:
# Add headers to serve security related headers
# Before enabling Strict-Transport-Security headers please read into this
# topic first.
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
ssl_protocols TLSv1.2;

# 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-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;
add_header Referrer-Policy no-referrer;

# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;

# The following 2 rules are only needed for the user_webfinger app.
# Uncomment it if you're planning to use this app.
#rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
#rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;

# The following rule is only needed for the Social app.
# Uncomment it if you're planning to use this app.
# rewrite ^/.well-known/webfinger /public.php?service=webfinger last;

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

# set max upload size
client_max_body_size 250M;
fastcgi_buffers 64 4K;
fastcgi_busy_buffers_size 8K;

# Enable gzip but do not remove ETag headers
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

location / {
   rewrite ^ /index.php$request_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/.+)\.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_param HTTPS on;
   fastcgi_param front_controller_active true;
   fastcgi_pass php-handler;
   fastcgi_intercept_errors on;
   fastcgi_request_buffering off;
}

location ~ ^/(?:updater|ocs-provider)(?:$|/) {
   try_files $uri/ =404;
   index index.php;
}

Please also make a backup of the .htaccess file from NC and put the following in your current one:

Code:
<IfModule mod_headers.c>
  <IfModule mod_setenvif.c>
    <IfModule mod_fcgid.c>
       SetEnvIfNoCase ^Authorization$ "(.+)" XAUTHORIZATION=$1
       RequestHeader set XAuthorization %{XAUTHORIZATION}e env=XAUTHORIZATION
    </IfModule>
    <IfModule mod_proxy_fcgi.c>
       SetEnvIfNoCase Authorization "(.+)" HTTP_AUTHORIZATION=$1
    </IfModule>
  </IfModule>

  <IfModule mod_env.c>
    # Add security and privacy related headers
    #Header set X-Content-Type-Options "nosniff"
    #Header set X-XSS-Protection "1; mode=block"
    #Header set X-Robots-Tag "none"
    #Header set X-Download-Options "noopen"
    #Header set X-Permitted-Cross-Domain-Policies "none"
    #Header set Referrer-Policy "no-referrer"
    SetEnv modHeadersAvailable true
  </IfModule>

  # Add cache control for static resources
  <FilesMatch "\.(css|js|svg|gif)$">
    Header set Cache-Control "max-age=15778463"
  </FilesMatch>

  # Let browsers cache WOFF files for a week
  <FilesMatch "\.woff2?$">
    Header set Cache-Control "max-age=604800"
  </FilesMatch>
</IfModule>
<IfModule mod_php7.c>
  php_value mbstring.func_overload 0
  php_value default_charset 'UTF-8'
  php_value output_buffering 0
  <IfModule mod_env.c>
    SetEnv htaccessWorking true
  </IfModule>
</IfModule>
<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{HTTP_USER_AGENT}  DavClnt
  RewriteRule ^$         /remote.php/webdav/          [L,R=302]
  RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
  RewriteRule ^\.well-known/host-meta /public.php?service=host-meta [QSA,L]
  RewriteRule ^\.well-known/host-meta\.json /public.php?service=host-meta-json [QSA,L]
  RewriteRule ^\.well-known/webfinger /public.php?service=webfinger [QSA,L]
  RewriteRule ^\.well-known/carddav /remote.php/dav/ [R=301,L]
  RewriteRule ^\.well-known/caldav /remote.php/dav/ [R=301,L]
  RewriteRule ^remote/(.*) remote.php [QSA,L]
  RewriteRule ^(?:build|tests|config|lib|3rdparty|templates)/.* - [R=404,L]
  RewriteCond %{REQUEST_URI} !^/\.well-known/(acme-challenge|pki-validation)/.*
  RewriteRule ^(?:\.|autotest|occ|issue|indie|db_|console).* - [R=404,L]
</IfModule>
<IfModule mod_mime.c>
  AddType image/svg+xml svg svgz
  AddEncoding gzip svgz
</IfModule>
<IfModule mod_dir.c>
  DirectoryIndex index.php index.html
</IfModule>
AddDefaultCharset utf-8
Options -Indexes
<IfModule pagespeed_module>
  ModPagespeed Off
</IfModule>
#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####

ErrorDocument 403 //
ErrorDocument 404 //

After that, reload your Nginx server and revisit your NC website. Now there should be no error about Headers anymore from NC and if you put it through the securityheaders.com, it should have a A+ rating.

Note: Don't worry about the "Feature-Policy" if that should popup in red! As mentioned before, the "Feature-Policy" is a thing you need to play around a little as this could have a impact on your NC installation and functionality.
 
If I set it that way, mynextcloud.com/ but mynextcloud.com/index.php will also be rate A+.
At index.php, it is rate A+ but duplicates.
 
Back
Top