• Please be aware: Kaspersky Anti-Virus has been deprecated
    With the upgrade to Plesk Obsidian 18.0.64, "Kaspersky Anti-Virus for Servers" will be automatically removed from the servers it is installed on. We recommend that you migrate to Sophos Anti-Virus for Servers.
  • The Horde webmail has been deprecated. Its complete removal is scheduled for April 2025. For details and recommended actions, see the Feature and Deprecation Plan.
  • We’re working on enhancing the Monitoring feature in Plesk, and we could really use your expertise! If you’re open to sharing your experiences with server and website monitoring or providing feedback, we’d love to have a one-hour online meeting with you.

Resolved How to set a cache control-header for html pages on nginx only

BenP

New Pleskian
Hey everybody,

I have a problem with the cache-control header in combination with the front-page files of Wordpress and nginx only. As I have a project with more than 30.000 files which only get updated twice a month I'd like to set a cache-control header of 600s on all front-page files so the pages get cached for 600 seconds in the browser. Now when I open one of those pages, I neither get a cache-control header nor an expires header so the pages would be cached forever in each browser (well, as long until the user would delete the browser cache - but proxies may cache them as well).

The cache-control header for .php files works great and is set automatically:
cache-control: no-cache, must-revalidate, max-age=0
expires: Wed, 11 Jan 1984 05:00:00 GMT
(By the way: Where does nginx get the information for the cache-control header for the .php-files?)

In Apache I used the following setting within .htaccess and everything worked great:
<IfModule mod_expires.c>
ExpiresByType text/html "access plus 10 minutes"
</IfModule>

Now I'm trying to use nginx only for the project and can't find the right setting.
I tried several combinations to get a cache-control header for those pages but none of them works for me:

location ~* \.(html|gz)$ {
expires 600s;
}

result: nothing changed

If I set expires "600"; within the server { } part, it would work as wished BUT the .php files get the new setting as well. The static files are served by a CDN, so they don't matter.

Maybe I just misunderstand how I can set a cache-control header for the front-page files and I can't use html|gz in the location part?

Best,
Benjamin

The current final config file looks as follows (no, the XXXXXXX are not present in the real config file :p):

/etc/nginx/nginx.conf:
http {
include mime.types;
default_type application/octet-stream;

gzip on;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript image/x-icon image/bmp image/svg+xml application/javascript;
gzip_vary on;
gzip_comp_level 2;
gzip_buffers 16 8k;

add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";

server_tokens off;

XXXXXXX

include /etc/nginx/conf.d/*.conf;
}


/var/www/vhosts/system/XXXXXXX/conf/nginx.conf:
server {
listen XXXXXXX ssl http2;

server_name XXXXXXX;
server_name XXXXXXX;
server_name XXXXXXX;
server_name XXXXXXX;
server_name XXXXXXX;

ssl_certificate /opt/psa/var/certificates/cert-XXXXXXX;
ssl_certificate_key /opt/psa/var/certificates/cert-XXXXXXX;
ssl_client_certificate /opt/psa/var/certificates/cert-XXXXXXX;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1h;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers XXXXXXX;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ecdh_curve XXXXXXX;

client_max_body_size 128m;
proxy_read_timeout 60;

root "/var/www/vhosts/XXXXXXX";
access_log "/var/www/vhosts/system/XXXXXXX/logs/proxy_access_ssl_log";
error_log "/var/www/vhosts/system/XXXXXXX/logs/proxy_error_log";

index index.php;


rewrite ^([^.]*[^/])$ $1/ permanent;

location / {
try_files $uri $uri/ /index.php?$args;
}

location ~ ^/(plesk-stat|awstats-icon|webstat|webstat-ssl|ftpstat|anon_ftpstat) {
auth_basic "Domain statistics";
auth_basic_user_file "/var/www/vhosts/system/XXXXXXX/pd/d..httpdocs@plesk-stat";
autoindex on;

location ~ ^/plesk-stat(.*) {
alias /var/www/vhosts/system/XXXXXXX/statistics/$1;
}

location ~ ^/awstats-icon(.*) {
alias /usr/share/awstats/icon/$1;
}

location ~ ^/(.*)/(.*) {
alias /var/www/vhosts/system/XXXXXXX/statistics/$1/$2;
}
}


location ~ ^/(wp-login\.php){
allow XXXXXXX/24;
allow XXXXXXX/24;
deny all;
error_page 403 = @wp_admin_ban;

fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass "unix:///var/www/vhosts/system/XXXXXXX/php-fpm.sock";
include /etc/nginx/fastcgi.conf;

}

location /wp-admin {
location ~ ^/(wp-admin/admin-ajax\.php) {
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass "unix:///var/www/vhosts/system/XXXXXXX/php-fpm.sock";
include /etc/nginx/fastcgi.conf;

}
location ~* /wp-admin/.*\.php$ {
allow XXXXXXX/24;
allow XXXXXXX/24;
deny all;
error_page 403 = @wp_admin_ban;
}
}

location @wp_admin_ban {
rewrite ^(.*) https://XXXXXXX permanent;
}


location ~ ^/~(.+?)(/.*?\.php)(/.*)?$ {
alias /var/www/vhosts/XXXXXXX/web_users/$1/$2;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass "unix:///var/www/vhosts/system/XXXXXXX/php-fpm.sock";
include /etc/nginx/fastcgi.conf;
}

location ~ \.php(/.*)?$ {
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass "unix:///var/www/vhosts/system/XXXXXXX/php-fpm.sock";
include /etc/nginx/fastcgi.conf;
}

}
 
Last edited:
Hi BenP,

if you desire to inform yourself, WHERE a setting has been done, pls. consider to use a FIND - search, as for example:

Code:
find /etc/nginx -type f -name "*.conf" -exec grep --color -Hni "cache-control" {} \;
find /usr/local/psa/admin/conf/templates -type f -name "*.php" -exec grep --color -Hni "cache-control" {} \;

Code:
find /var/www/vhosts/system/*/conf -type f -name "*.conf" -exec grep --color -Hni "cache-control" {} \;


location ~* \.(html|gz)$ {
expires 600s;
}
This is insufficient, as you should use instead ( for example! ):
Code:
location ~ \.(html|gz)$ {
    expires 600s;
    add_header Pragma "public";
    add_header Cache-Control "max-age=600, public, must-revalidate, proxy-revalidate";
}
Pls note, that additional nginx directives for a specific (sub)domain should be placed at:

=> HOME > Domains > (sub)YOUR-DOMAIN.COM > Apache & nginx Settings > ( option/textbox ) Additional nginx directives

In addition, pls inform yourself about the "proxy_ignore_headers" options, with which you are able to fine-tune (sub)domain specific settings, if you desire something different than for you global nginx - settings: => Module ngx_http_proxy_module
 
Hi UFHH01,

Thank you for your quick help.


Code:
find /etc/nginx -type f -name "*.conf" -exec grep --color -Hni "cache-control" {} \;
find /usr/local/psa/admin/conf/templates -type f -name "*.php" -exec grep --color -Hni "cache-control" {} \;

Code:
find /var/www/vhosts/system/*/conf -type f -name "*.conf" -exec grep --color -Hni "cache-control" {} \;
I'm still not yet familiar with all those commands in Ubuntu so thank you for that.

This is insufficient, as you should use instead ( for example! ):
Code:
location ~ \.(html|gz)$ {
    expires 600s;
    add_header Pragma "public";
    add_header Cache-Control "max-age=600, public, must-revalidate, proxy-revalidate";
}
I changed the code but still, I can't see any cache-control or expires header on the .html files no matter where I put the location part. I reconfigured all with /usr/local/psa/admin/bin/httpdmng --reconfigure-all and restarted nginx but nothing has changed.

Okay, maybe I misunderstand something. If I upload a .html file and open it in the browser, the headers appear now. But I want to set that header on all pages, which are generated by Wordpress. On those pages, I don't see the additional headers.

Another issue I see now: The uploaded .html file shows "GZIP is not enabled" on Check GZIP compression, but the Wordpress files do.

Pls note, that additional nginx directives for a specific (sub)domain should be placed at:

=> HOME > Domains > (sub)YOUR-DOMAIN.COM > Apache & nginx Settings > ( option/textbox ) Additional nginx directives
I already did that before but to understand how nginx works and which parameters to put on top, for now I do some testing with a custom nginxDomainVirtualHost.php file.

In addition, pls inform yourself about the "proxy_ignore_headers" options, with which you are able to fine-tune (sub)domain specific settings, if you desire something different than for you global nginx - settings: => Module ngx_http_proxy_module
Thanks for the additional information. So far I have only two projects and both use the same settings.

Best, Benjamin
 
Last edited:
Hi BenP,

for now I do some testing with a custom nginxDomainVirtualHost.php file.
Sorry... wrong place for tests. Exactly for your described situation, there are the "Additional nginx directives" options. ;)
 
Hi UFHH01,

luckily it's not on a productive server :D.

I changed the code but still, I can't see any cache-control or expires header on the .html files.

If I upload a .html file and open it in the browser, the headers appear now. But I want to set that header on all pages, which are generated by Wordpress. On those pages, I don't see the additional headers.
Also the following headers are missing on the uploaded.html file:
  1. content-encoding: gzip
  2. content-type: text/html; charset=UTF-8
  3. vary: Accept-Encoding
  4. x-frame-options: SAMEORIGIN
  5. x-xss-protection: 1; mode=block
Seems like Plesk still tries to serve the files by Apache?
 
Last edited:
Hi UFHH01,

I just tried to set-up a new Wordpress site with exactly the same settings on the Plesk panel to post the link here (other demo pages are behind a firewall) but on the clean Wordpress install everything works fine.

There seems to be an issue with Wordpress itself handling the files which I have to resolve on my own.

Thank you for your patience and help.

Best,
Benjamin
 
Back
Top