• 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

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