NGINX + Apache to NGINX Only Migration: Subdomain Routing Issue with PrestaShop and WordPress Directories

I reinstalled NGINX + Apache, switching to NGINX alone. It works much better. I have PrestaShop and WordPress websites on the server. Everything works great.
The problem occurs with one specific subdomain:

demo.site.com

There are many PS/WP pages in directories such as:
/wordpress_x/ (wordpress)
/wordpress_xxx/ (wordpress)
/cc_ps_prestashop_xx/ (prestashop)
/cc_ps_prestashop_xx/ (prestashop)
/xxx/ (wordpress)

I created a new template that, depending on the directory, loads NGINX settings for Presta or WP. In short, directories for PS ALWAYS start with cc_ps, while all others are WP.

Unfortunately, it doesn’t work :frowning:

My .stpl template:

#=========================================================================#
# Hybrid Demo Template - WordPress + PrestaShop                          #
# DO NOT MODIFY THIS FILE! CHANGES WILL BE LOST WHEN REBUILDING DOMAINS   #
# https://hestiacp.com/docs/server-administration/web-templates.html      #
#=========================================================================#

server {
	listen      %ip%:%web_ssl_port% ssl;
	server_name %domain_idn% %alias_idn%;
	root        %sdocroot%;
	index       index.php index.html index.htm;
	access_log  /var/log/nginx/domains/%domain%.log combined;
	access_log  /var/log/nginx/domains/%domain%.bytes bytes;
	error_log   /var/log/nginx/domains/%domain%.error.log error;

	ssl_certificate     %ssl_pem%;
	ssl_certificate_key %ssl_key%;
	ssl_stapling        on;
	ssl_stapling_verify on;

	# TLS 1.3 0-RTT anti-replay
	if ($anti_replay = 307) { return 307 https://$host$request_uri; }
	if ($anti_replay = 425) { return 425; }

	include %home%/%user%/conf/web/%domain%/nginx.hsts.conf*;

	location = /favicon.ico {
		log_not_found off;
		access_log off;
	}

	location = /robots.txt {
		try_files $uri $uri/ /index.php?$args;
		log_not_found off;
		access_log off;
	}

	location ~ /\.(?!well-known\/) {
		deny all;
		return 404;
	}

	# =================================================================
	# PRESTASHOP RULES - dla katalogów zaczynających się od cc_ps
	# =================================================================
	
	# PrestaShop - Force pdf files to be downloaded
	location ~ ^/cc_ps.*\.pdf$ {
		add_header Content-Disposition Attachment;
		add_header X-Content-Type-Options nosniff;
		try_files $uri $uri/ /index.php?$args;
	}

	# PrestaShop - Force files in upload directory to be downloaded
	location ~ ^/cc_ps.*/upload/ {
		add_header Content-Disposition Attachment;
		add_header X-Content-Type-Options nosniff;
		
		location ~ \.php$ {
			deny all;
			return 404;
		}
	}

	# PrestaShop - Images rewrite rules for cc_ps directories
	location ~ ^/cc_ps.*?/([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.jpg$ {
		try_files $uri /cc_ps/img/p/$1/$1$2$3.jpg /index.php?$args;
	}
	location ~ ^/cc_ps.*?/([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.jpg$ {
		try_files $uri /cc_ps/img/p/$1/$2/$1$2$3$4.jpg /index.php?$args;
	}
	location ~ ^/cc_ps.*?/([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.jpg$ {
		try_files $uri /cc_ps/img/p/$1/$2/$3/$1$2$3$4$5.jpg /index.php?$args;
	}
	location ~ ^/cc_ps.*?/([0-9])([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.jpg$ {
		try_files $uri /cc_ps/img/p/$1/$2/$3/$4/$1$2$3$4$5$6.jpg /index.php?$args;
	}
	location ~ ^/cc_ps.*?/([0-9])([0-9])([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.jpg$ {
		try_files $uri /cc_ps/img/p/$1/$2/$3/$4/$5/$1$2$3$4$5$6$7.jpg /index.php?$args;
	}
	location ~ ^/cc_ps.*?/([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.jpg$ {
		try_files $uri /cc_ps/img/p/$1/$2/$3/$4/$5/$6/$1$2$3$4$5$6$7$8.jpg /index.php?$args;
	}
	location ~ ^/cc_ps.*?/([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.jpg$ {
		try_files $uri /cc_ps/img/p/$1/$2/$3/$4/$5/$6/$7/$1$2$3$4$5$6$7$8$9.jpg /index.php?$args;
	}
	location ~ ^/cc_ps.*?/([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])(-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.jpg$ {
		try_files $uri /cc_ps/img/p/$1/$2/$3/$4/$5/$6/$7/$8/$1$2$3$4$5$6$7$8$9$10.jpg /index.php?$args;
	}
	location ~ ^/cc_ps.*?/c/([0-9]+)(-[.*_a-zA-Z0-9-]*)(-[0-9]+)?/.+\.jpg$ {
		try_files $uri /cc_ps/img/c/$1$2$3.jpg /index.php?$args;
	}
	location ~ ^/cc_ps.*?/c/([a-zA-Z_-]+)(-[0-9]+)?/.+\.jpg$ {
		try_files $uri /cc_ps/img/c/$1$2.jpg /index.php?$args;
	}

	# PrestaShop - AlphaImageLoader for IE and fancybox
	location ~ ^/cc_ps.*?/images_ie/?([^/]+)\.(jpe?g|png|webp|gif)$ {
		try_files $uri /cc_ps/js/jquery/plugins/fancybox/images/$1.$2 /index.php?$args;
	}

	# PrestaShop - Web service API
	location ~ ^/cc_ps.*?/api/?(.*)$ {
		try_files $uri /cc_ps/webservice/dispatcher.php?url=$1;
	}

	# PrestaShop - Installation sandbox
	location ~ ^/cc_ps.*?(/install(?:-dev)?/sandbox)/(.*) {
		try_files $uri /cc_ps$1/test.php;
	}

	# PrestaShop - Source code directories protection
	location ~ ^/cc_ps.*?/(app|bin|cache|classes|config|controllers|docs|localization|override|src|tests|tools|translations|travis-scripts|vendor|var)/ {
		deny all;
		return 404;
	}

	# PrestaShop - vendor in modules directory
	location ~ ^/cc_ps.*?/modules/.*/vendor/ {
		deny all;
		return 404;
	}

	# PrestaShop - Prevent exposing sensitive files
	location ~ ^/cc_ps.*?\.(yml|log|tpl|twig|sass)$ {
		deny all;
		return 404;
	}

	# PrestaShop - img directory protection
	location ~ ^/cc_ps.*?/img/ {
		location ~ \.php$ {
			deny all;
			return 404;
		}
		try_files $uri $uri/ /index.php?$args;
	}

	# PrestaShop directories - main handler
	location ~ ^/cc_ps {
		try_files $uri $uri/ /index.php?$args;

		location ~* ^.+\.(ogg|ogv|svg|svgz|swf|eot|otf|woff|woff2|mov|mp3|mp4|webm|flv|ttf|rss|atom|jpg|jpeg|gif|png|webp|ico|bmp|mid|midi|wav|rtf|css|js|jar)$ {
			expires 30d;
			fastcgi_hide_header "Set-Cookie";
		}

		location ~ [^/]\.php(/|$) {
			try_files $fastcgi_script_name /index.php$uri&$args =404;

			include /etc/nginx/fastcgi_params;

			fastcgi_index index.php;
			fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;
			fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
			fastcgi_split_path_info ^(.+\.php)(/.+)$;

			fastcgi_pass %backend_lsnr%;

			include %home%/%user%/conf/web/%domain%/nginx.fastcgi_cache.conf*;
		}
	}

	# =================================================================
	# WORDPRESS RULES - dla wszystkich pozostałych katalogów
	# =================================================================
	
	location / {
		try_files $uri $uri/ /index.php?$args;

		location ~* ^.+\.(ogg|ogv|svg|svgz|swf|eot|otf|woff|woff2|mov|mp3|mp4|webm|flv|ttf|rss|atom|jpg|jpeg|gif|png|webp|ico|bmp|mid|midi|wav|rtf|css|js|jar)$ {
			expires 30d;
			fastcgi_hide_header "Set-Cookie";
		}

		# WordPress - Block PHP in uploads/files directories
		location ~* /(?:uploads|files)/.*.php$ {
			deny all;
			return 404;
		}

		location ~ [^/]\.php(/|$) {
			try_files $uri =404;

			include /etc/nginx/fastcgi_params;

			fastcgi_index index.php;
			fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;
			fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

			fastcgi_pass %backend_lsnr%;

			include %home%/%user%/conf/web/%domain%/nginx.fastcgi_cache.conf*;

			# WordPress cache rules
			if ($request_uri ~* "/wp-admin/|/wp-json/|wp-.*.php|xmlrpc.php|index.php|/store.*|/cart.*|/my-account.*|/checkout.*") {
				set $no_cache 1;
			}

			if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in|woocommerce_items_in_cart|woocommerce_cart_hash|PHPSESSID") {
				set $no_cache 1;
			}
		}
	}

	# =================================================================
	# COMMON RULES
	# =================================================================

	# Error pages
	error_page 403 /error/404.html;
	error_page 404 /index.php?controller=404;
	error_page 500 502 503 504 /error/50x.html;

	location /error/ {
		alias %home%/%user%/web/%domain%/document_errors/;
	}

	location /vstats/ {
		alias   %home%/%user%/web/%domain%/stats/;
		include %home%/%user%/web/%domain%/stats/auth.conf*;
	}

	proxy_hide_header Upgrade;

	include /etc/nginx/conf.d/phpmyadmin.inc*;
	include /etc/nginx/conf.d/phppgadmin.inc*;
	include %home%/%user%/conf/web/%domain%/nginx.ssl.conf_*;
}

Okay, I managed to solve the problem. I’m pasting the template here—maybe someone will find it useful :slight_smile:

#=========================================================================#
# Hybrid Demo Template - WordPress + PrestaShop                          #
# DO NOT MODIFY THIS FILE! CHANGES WILL BE LOST WHEN REBUILDING DOMAINS   #
# https://hestiacp.com/docs/server-administration/web-templates.html      #
#=========================================================================#

server {
    listen      %ip%:%web_ssl_port% ssl;
    server_name %domain_idn% %alias_idn%;
    root        %sdocroot%;
    index       index.php index.html index.htm;
    access_log  /var/log/nginx/domains/%domain%.log combined;
    access_log  /var/log/nginx/domains/%domain%.bytes bytes;
    error_log   /var/log/nginx/domains/%domain%.error.log error;

    ssl_certificate     %ssl_pem%;
    ssl_certificate_key %ssl_key%;
    ssl_stapling        on;
    ssl_stapling_verify on;

    # TLS 1.3 0-RTT anti-replay
    if ($anti_replay = 307) { return 307 https://$host$request_uri; }
    if ($anti_replay = 425) { return 425; }

    include %home%/%user%/conf/web/%domain%/nginx.hsts.conf*;

    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    location = /robots.txt {
        try_files $uri $uri/ /index.php$is_args$args;
        log_not_found off;
        access_log off;
    }

    location ~ /\.(?!well-known) {
        deny all;
    }

    # =================================================================
    # PRESTASHOP RULES - Global rewrites (Official PS9 format)
    # =================================================================

    # Images
    rewrite ^/(cc_ps[^/]*)/(\d)(-[\w-]+)?/.+\.jpg$ /$1/img/p/$2/$2$3.jpg last;
    rewrite ^/(cc_ps[^/]*)/(\d)(\d)(-[\w-]+)?/.+\.jpg$ /$1/img/p/$2/$3/$2$3$4.jpg last;
    rewrite ^/(cc_ps[^/]*)/(\d)(\d)(\d)(-[\w-]+)?/.+\.jpg$ /$1/img/p/$2/$3/$4/$2$3$4$5.jpg last;
    rewrite ^/(cc_ps[^/]*)/(\d)(\d)(\d)(\d)(-[\w-]+)?/.+\.jpg$ /$1/img/p/$2/$3/$4/$5/$2$3$4$5$6.jpg last;
    rewrite ^/(cc_ps[^/]*)/(\d)(\d)(\d)(\d)(\d)(-[\w-]+)?/.+\.jpg$ /$1/img/p/$2/$3/$4/$5/$6/$2$3$4$5$6$7.jpg last;
    rewrite ^/(cc_ps[^/]*)/(\d)(\d)(\d)(\d)(\d)(\d)(-[\w-]+)?/.+\.jpg$ /$1/img/p/$2/$3/$4/$5/$6/$7/$2$3$4$5$6$7$8.jpg last;
    rewrite ^/(cc_ps[^/]*)/(\d)(\d)(\d)(\d)(\d)(\d)(\d)(-[\w-]+)?/.+\.jpg$ /$1/img/p/$2/$3/$4/$5/$6/$7/$8/$2$3$4$5$6$7$8$9.jpg last;
    rewrite ^/(cc_ps[^/]*)/(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(-[\w-]+)?/.+\.jpg$ /$1/img/p/$2/$3/$4/$5/$6/$7/$8/$9/$2$3$4$5$6$7$8$9$10.jpg last;
    rewrite ^/(cc_ps[^/]*)/c/([\w.-]+)/.+\.jpg$ /$1/img/c/$2.jpg last;

    # AlphaImageLoader for IE and FancyBox
    rewrite ^/(cc_ps[^/]*)/images_ie/?([^/]+)\.(gif|jpe?g|png)$ /$1/js/jquery/plugins/fancybox/images/$2.$3 last;

    # Web service API
    rewrite ^/(cc_ps[^/]*)/api/?(.*)$ /$1/webservice/dispatcher.php?url=$2 last;

    # Installation sandbox
    rewrite ^/(cc_ps[^/]*)/install(?:-dev)?/sandbox/(.*) /$1/install/sandbox/test.php last;

    # =================================================================
    # PRESTASHOP SPECIFIC LOCATIONS
    # =================================================================

    # PrestaShop admin directories
    location ~ ^/(cc_ps[^/]*)/admin[^/]*/ {
        try_files $uri $uri/ /$1/admin/index.php$is_args$args;
    }

    # Source code directories
    location ~ ^/(cc_ps[^/]*)/(app|bin|cache|classes|config|controllers|docs|localization|override|src|tests|tools|translations|var|vendor)/ {
        deny all;
    }

    # vendor in modules directory
    location ~ ^/(cc_ps[^/]*)/modules/.*/vendor/ {
        deny all;
    }

    # Prevent exposing other sensitive files
    location ~ ^/(cc_ps[^/]*)/.*\.(log|tpl|twig|sass|yml)$ {
        deny all;
    }

    # Prevent injection of PHP files
    location ~ ^/(cc_ps[^/]*)/img/ {
        location ~ \.php$ { deny all; }
    }
    location ~ ^/(cc_ps[^/]*)/upload/ {
        location ~ \.php$ { deny all; }
    }

    # =================================================================
    # MAIN LOCATION HANDLERS  
    # =================================================================

    # PrestaShop directories - exact copy of official PS9 config
    location ~ ^/(cc_ps[^/]*) {
        try_files $uri $uri/ /$1/index.php$is_args$args;

        location ~* ^.+\.(ogg|ogv|svg|svgz|swf|eot|otf|woff|woff2|mov|mp3|mp4|webm|flv|ttf|rss|atom|jpg|jpeg|gif|png|webp|ico|bmp|mid|midi|wav|rtf|css|js|jar)$ {
            expires 30d;
            fastcgi_hide_header "Set-Cookie";
        }

        location ~ [^/]\.php(/|$) {
            # Official PrestaShop 9 PHP handler
            fastcgi_split_path_info ^(.+?\.php)(/.*)$;
            try_files $fastcgi_script_name =404;

            include /etc/nginx/fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $request_filename;
            fastcgi_index index.php;
            fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

            fastcgi_pass %backend_lsnr%;

            include %home%/%user%/conf/web/%domain%/nginx.fastcgi_cache.conf*;
        }
    }

    # =================================================================
    # WORDPRESS CC_* DIRECTORIES (not cc_ps*)
    # =================================================================
    location ~ ^/(cc_(?!ps)[^/]*) {
        # WordPress official rule for subdirectory installations
        try_files $uri $uri/ /$1/index.php?$args;
        
        location ~* ^.+\.(ogg|ogv|svg|svgz|swf|eot|otf|woff|woff2|mov|mp3|mp4|webm|flv|ttf|rss|atom|jpg|jpeg|gif|png|webp|ico|bmp|mid|midi|wav|rtf|css|js|jar)$ {
            expires 30d;
            fastcgi_hide_header "Set-Cookie";
        }

        # WordPress - Block PHP in uploads/files directories (official security rule)
        location ~* /(?:uploads|files)/.*\.php$ {
            deny all;
        }

        location ~ \.php$ {
            try_files $uri =404;
            include /etc/nginx/fastcgi_params;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_pass %backend_lsnr%;
            include %home%/%user%/conf/web/%domain%/nginx.fastcgi_cache.conf*;

            # WordPress cache rules (from official docs)
            if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php") {
                set $no_cache 1;
            }
            if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
                set $no_cache 1;
            }
        }
    }

    # =================================================================
    # COMMON RULES
    # =================================================================

    error_page 404 /index.php?controller=404;
    error_page 500 502 503 504 /error/50x.html;

    location /error/ {
        alias %home%/%user%/web/%domain%/document_errors/;
    }

    location /vstats/ {
        alias   %home%/%user%/web/%domain%/stats/;
        include %home%/%user%/web/%domain%/stats/auth.conf*;
    }

    proxy_hide_header Upgrade;

    include /etc/nginx/conf.d/phpmyadmin.inc*;
    include /etc/nginx/conf.d/phppgadmin.inc*;
    include %home%/%user%/conf/web/%domain%/nginx.ssl.conf_*;
}
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.