name : NginxconfmakerModel.php
<?php
/**
 * @package   admintools
 * @copyright Copyright (c)2010-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Component\AdminTools\Administrator\Model;

defined('_JEXEC') or die;

use Akeeba\Component\AdminTools\Administrator\Helper\CloudIPRanges;
use Akeeba\Component\AdminTools\Administrator\Helper\ServerTechnology;
use DateTimeZone;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;

#[\AllowDynamicProperties]
class NginxconfmakerModel extends ServerconfigmakerModel
{

	/**
	 * The current configuration of this feature
	 *
	 * @var  object
	 */
	protected $configKey = 'nginxconfig';

	/**
	 * The base name of the configuration file being saved by this feature, e.g. ".htaccess". The file is always saved
	 * in the site's root. Any old files under that name are renamed with a .admintools suffix.
	 *
	 * @var string
	 */
	protected $configFileName = 'nginx.conf';

	/** @inheritdoc  */
	protected $usePublicRoot = false;

	/** @inheritdoc  */
	public function isSupported(): int
	{
		return ServerTechnology::isNginxSupported();
	}

	/**
	 * Compile and return the contents of the NginX configuration file
	 *
	 * @return  string
	 */
	public function makeConfigFile()
	{
		// Make sure we are called by an expected caller
		ServerTechnology::checkCaller($this->allowedCallersForMake);

		$app = Factory::getApplication();

		$timezone = 'UTC';

		// Fetch the timezone from the user only if we're not in CLI
		if (!$app->isClient('cli'))
		{
			$timezone = $app->getIdentity()->getParam('timezone', $app->get('offset', 'UTC'));
		}

		$date = clone Factory::getDate();
		$tz   = new DateTimeZone($timezone);
		$date->setTimezone($tz);
		$d       = $date->format('Y-m-d H:i:s T', true);
		$version = ADMINTOOLS_VERSION;

		$config = (object) $this->loadConfiguration();

		// Load the fastcgi_pass setting
		$fastcgi_pass_block = $config->fastcgi_pass_block;

		if (empty($fastcgi_pass_block))
		{
			$fastcgi_pass_block = 'fastcgi_pass   127.0.0.1:9000;';
		}

		$fastcgi_pass_block = trim($fastcgi_pass_block);

		// Get the directory to the site's root
		$rewritebase      = $config->rewritebase;
		$rewritebaseSlash = '/' . trim($rewritebase, '/ ');
		$rewritebaseSlash = ($rewritebaseSlash == '/') ? '' : $rewritebaseSlash;
		$rewritebase      = '/' . trim($rewritebase, '/ ');

		$nginxConf = <<<END
### ===========================================================================
### Security Enhanced & Highly Optimized NginX Configuration File for Joomla!
### automatically generated by Admin Tools $version on $d
### ===========================================================================
###
### Admin Tools is Free Software, distributed under the terms of the GNU
### General Public License version 3 or, at your option, any later version
### published by the Free Software Foundation.
###
### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
### !!                                                                       !!
### !!  If you get an Internal Server Error 500 or a blank page when trying  !!
### !!  to access your site, remove this file and try tweaking its settings  !!
### !!  in the back-end of the Admin Tools component.                        !!
### !!                                                                       !!
### !!  Remember to include this file in your site's configuration file.     !!
### !!  Also remember to reload or restart NginX after making any change to  !!
### !!  this file.                                                           !!
### !!                                                                       !!
### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
###

### Prevent access to this file
location = $rewritebaseSlash/nginx.conf {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

location = $rewritebaseSlash/nginx.conf.admintools {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

# Unified HTTP/HTTPS protocol detection into the \$akActualProto variable 
set \$akActualProto "http";

if (\$http_x_forwarded_proto = "https") {
    set \$akActualProto "https";
}

if (\$scheme = "https") {
    set \$akActualProto "https";
}

END;

		// Let's start with IP restriction
		$restrictIP = $config->restrictip ?? 'none';

		if ($restrictIP !== 'none')
		{
			$restrictIPList = CloudIPRanges::getIPRanges($restrictIP);

			if ($restrictIPList === null)
			{
				throw new \RuntimeException(Text::_('COM_ADMINTOOLS_ERR_INVALID_RESTRICTIP'));
			}

			$nginxConf .= <<< END

location $rewritebaseSlash/ {
	##### Restricted access by IP address -- BEGIN

END;

			foreach ($restrictIPList as $ip)
			{
				$nginxConf .= '    allow ' . $ip . ";\n";
			}

			$nginxConf .= <<< END
	deny all;
	##### Restricted access by IP address -- END
	
	# Joomla public frontend application
	try_files \$uri \$uri/ $rewritebaseSlash/index.php?\$args;
}

END;
		}

		// Protect against common file injection attacks?
		if ($config->fileinj == 1)
		{
			$nginxConf .= <<< CONFDATA
######################################################################
## Protect against common file injection attacks
######################################################################
set \$file_injection 0;
if (\$query_string ~ "[a-zA-Z0-9_]=http://") {
	set \$file_injection 1;
}
if (\$query_string ~ "[a-zA-Z0-9_]=(\.\.//?)+") {
	set \$file_injection 1;
}
if (\$query_string ~ "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+") {
	set \$file_injection 1;
}
if (\$file_injection = 1) {
	return 403;
	break;
}

CONFDATA;
		}

		if ($config->leftovers == 1)
		{
			$nginxConf .= <<<END
######################################################################
## Block access to configuration.php-dist and htaccess.txt
######################################################################
location = $rewritebaseSlash/configuration.php-dist {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

location = $rewritebaseSlash/htaccess.txt {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

location = $rewritebaseSlash/web.config {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

location = $rewritebaseSlash/configuration.php {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

location = $rewritebaseSlash/CONTRIBUTING.md {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

location = $rewritebaseSlash/joomla.xml {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

location = $rewritebaseSlash/LICENSE.txt {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

location = $rewritebaseSlash/phpunit.xml {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

location = $rewritebaseSlash/README.txt {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

location = $rewritebaseSlash/web.config.txt {
	log_not_found off;
	access_log off;
	return 404;
	break;
}

END;
		}

		if ($config->clickjacking == 1)
		{
			$nginxConf .= <<< ENDCONF
## Protect against clickjacking
add_header X-Frame-Options SAMEORIGIN;

ENDCONF;
		}

		if (!empty($config->hoggeragents) && ($config->nohoggers == 1))
		{
			$nginxConf .= <<< ENDCONF
######################################################################
## Block access from specific user agents
######################################################################
set \$bad_ua 0;

ENDCONF;

			foreach ($config->hoggeragents as $agent)
			{
				$nginxConf .= <<< ENDCONF
if (\$http_user_agent ~ "$agent") {
	set \$bad_ua 1;
}

ENDCONF;
			}

			$nginxConf .= <<< ENDCONF
if (\$bad_ua = 1) {
	return 403;
}

ENDCONF;
		}

		if (($config->fileorder == 1) || ($config->nodirlists == 1))
		{
			$nginxConf .= <<<ENDCONF
######################################################################
## Directory indices and no automatic directory listings
## Forces index.php to be read before the index.htm(l) files
## Also disables showing files in a directory automatically
######################################################################
index index.php index.html index.htm;

ENDCONF;
		}

		if ($config->symlinks != 0)
		{
			$nginxConf .= <<<ENDCONF
######################################################################
## Disable following symlinks
######################################################################

ENDCONF;
			switch ($config->symlinks)
			{
				case 1:
					$nginxConf .= "disable_symlinks on;\n";
					break;

				case 2:
					$nginxConf .= "disable_symlinks if_not_owner;\n";
					break;
			}
		}

		if ($config->exptime != 0)
		{
			$expWeek = '1w';
			$expWeekText = '1 week';
			$expMonth = '1M';
			$expMonthText = '1 month';

			if ($config->exptime == 2)
			{
				$expWeek = '1y';
				$expWeekText = '1 year';
				$expMonth = '1y';
				$expMonthText = '1 year';
			}

			$nginxConf .= <<<ENDCONF
######################################################################
## Set default expiration time
######################################################################

# No caching for specific resource types
location ~* \.(appcache|mf)$ {
		access_log off; log_not_found off;
		expires epoch;
}

location ~* \.(json|xml)$ {
		access_log off; log_not_found off;
		expires epoch;
}

# RSS and Atom feeds : 1 hour (hardcoded)
location ~* \.(rss|atom)$ {
		access_log off; log_not_found off;
		expires 1h;
		add_header Cache-Control "public";
}

# CSS and JavaScript : $expWeekText
location ~* \.(css|js|jsonld)$ {
		access_log off; log_not_found off;
		expires $expWeek;
		add_header Cache-Control "public";
}

# Image files : $expMonthText
location ~* \.(bmp|gif|jpg|jpeg|jp2|jfif|png|svg|tif|tiff|ico|wbmp|wbxml|smil|webp)$ {
		access_log off; log_not_found off;
		expires $expMonth;
		add_header Cache-Control "public";
}

# Font files : $expMonthText
location ~* \.(woff|woff2|ttf|otf|eot)$ {
		access_log off; log_not_found off;
		expires $expMonth;
		add_header Cache-Control "public";
}

# Audio files : $expMonthText
location ~* \.(3gp|3g2|aac|mid|midi|mp3|m4a|m4r|aif|aiff|ra|ram|wav|voc|ogg|ogx|opus|aif|aiff|aifc|m3u|m3u8)$ {
		access_log off; log_not_found off;
		expires $expMonth;
		add_header Cache-Control "public";
}

# Video files : $expMonthText
location ~* \.(swf|vrml|avi|mkv|mpg|mpeg|mp4|m4v|mov|asf)$ {
		access_log off; log_not_found off;
		expires $expMonth;
		add_header Cache-Control "public";
}

ENDCONF;
		}

		if ($config->autocompress == 1)
		{
			$nginxConf .= <<<ENDCONF
######################################################################
## Automatic compression of static resources
## Compress text, html, javascript, css, xml and other static resources
## May kill access to your site for old versions of Internet Explorer
######################################################################
# The following is the actual automatic compression setup
gzip            on;
gzip_vary		on;
gzip_comp_level 6;
gzip_proxied	expired no-cache no-store private auth;
gzip_min_length 1000;
gzip_http_version 1.1;
gzip_types      text/plain text/css application/xhtml+xml application/xml+rss application/rss+xml application/x-javascript application/javascript text/javascript application/json text/xml application/xml image/svg+xml;
gzip_buffers    16 8k;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";

ENDCONF;
		}

		if ($config->etagtype != -1)
		{
			$etagValue = ($config->etagtype == 1) ? 'on' : 'off';
			$nginxConf .= <<< CONF
## Send ETag (you have set it to '$etagValue')
etag $etagValue;

CONF;

		}

		$host = strtolower($config->httphost);

		if (substr($host, 0, 4) == 'www.')
		{
			$wwwHost   = $host;
			$noWwwHost = substr($host, 4);
		}
		else
		{
			$noWwwHost = $host;
			$wwwHost   = 'www.' . $host;
		}

		$subfolder = trim($config->rewritebase, '/') ? trim($config->rewritebase, '/') . '/' : '';

		switch ($config->wwwredir)
		{
			case 1:
				// non-www to www
				$nginxConf .= <<<END
######################################################################
## Redirect non-www to www
######################################################################
if (\$host = '$noWwwHost' ) {
	rewrite ^/(.*)$ \$akActualProto://$wwwHost/$subfolder$1 permanent;
}

END;
				break;

			case 2:
				// www to non-www
				$nginxConf .= <<<END
######################################################################
## Redirect www to non-www
######################################################################
if (\$host = '$wwwHost' ) {
	rewrite ^/(.*)$ \$akActualProto://$noWwwHost/$subfolder$1 permanent;
}

END;
				break;
		}

		if (!empty($config->olddomain))
		{
			$nginxConf .= <<<END
######################################################################
## Redirect old to new domains
######################################################################

END;
			$domains   = trim($config->olddomain);
			$domains   = explode(',', $domains);
			$newdomain = $config->httphost;

			foreach ($domains as $olddomain)
			{
				$olddomain = trim($olddomain);

				if (empty($olddomain))
				{
					continue;
				}

				$olddomain = $this->escape_string_for_regex($olddomain);

				$nginxConf .= <<<END
if (\$host ~ "$olddomain$" ) {
	rewrite ^/(.*)$ \$akActualProto://$newdomain/$1 permanent;
}

END;
			}
		}

		if ($config->hstsheader == 1)
		{
			$nginxConf .= <<<END
## HSTS Header - See http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
add_header Strict-Transport-Security "max-age=31536000" always;

END;
		}
		elseif ($config->hstsheader == 2)
		{
			$nginxConf .= <<<END
## HSTS Header - See http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

END;
		}

		if ($config->cors == 1)
		{
			$nginxConf .= <<<END
## Explicitly enable Cross-Origin Resource Sharing (CORS)
add_header Access-Control-Allow-Origin "*";
add_header Timing-Allow-Origin "*";

END;
		}
		elseif ($config->cors == -1)
		{
			$nginxConf .= <<<END
## Explicitly disable Cross-Origin Resource Sharing (CORS)
add_header Cross-Origin-Resource-Policy "same-origin";

END;
		}

		if ($config->referrerpolicy !== '-1')
		{
			$nginxConf .= <<<END
## Referrer-policy
add_header Referrer-Policy "{$config->referrerpolicy}";

END;
		}

		if ($config->notracetrack == 1)
		{
			$nginxConf .= <<<END
## Disable HTTP methods TRACE and TRACK (protect against XST)
if (\$request_method ~ ^(TRACE|TRACK)$ ) {
	return 405;
}

END;
		}

		if ($config->reducemimetyperisks == 1)
		{
			$nginxConf .= <<< ENDCONF
## Reduce MIME type security risks
add_header X-Content-Type-Options "nosniff";

ENDCONF;
		}

		if ($config->reflectedxss == 1)
		{
			$nginxConf .= <<< ENDCONF
## Reflected XSS prevention
add_header X-XSS-Protection "1; mode=block";

ENDCONF;
		}

		if ($config->svgneutralise) {
			$nginxConf .= <<< ENDCONF
## Neutralize scripts in SVG files
location ~* "\.svg$" {
	add_header Content-Security-Policy "script-src 'none'"
}

ENDCONF;
		}


		if ($config->noserversignature == 1)
		{
			$nginxConf .= <<< ENDCONF
## Remove NginX and PHP version signature
add_header X-Powered-By "";
add_header X-Content-Powered-By "";

ENDCONF;
		}

		if ($config->notransform == 1)
		{
			$nginxConf .= <<< ENDCONF
## Prevent content transformation
add_header Cache-Control "no-transform";

ENDCONF;
		}


		if ($config->cfipfwd == 1)
		{
			$trustedSources = '';

			if (!empty($config->proxy_ips))
			{
				$trustedSources = "## User-supplied reverse proxy IP address(es)\n" .
					implode("\n", array_map(function ($x) {
						return 'set_real_ip_from ' . $x . ';';
				}, array_filter($config->proxy_ips, function ($x) {
						return !empty($x);
						})
					)
				);
			}

			$nginxConf .= <<<END
######################################################################
## Real IP forwarding support 
######################################################################
$trustedSources
real_ip_header     X-Forwarded-For;

END;
		}

		if ($config->opttimeout == 1)
		{
			$nginxConf .= <<<END
# -- Timeout handling, see http://wiki.nginx.org/HttpCoreModule
client_header_timeout 10;
client_body_timeout   10;
send_timeout          30;
keepalive_timeout     30s;

END;
		}

		if ($config->optsockets == 1)
		{
			$nginxConf .= <<<END
# -- Socket settings, see http://wiki.nginx.org/HttpCoreModule
connection_pool_size        8192;
client_header_buffer_size   4k;
large_client_header_buffers 8 8k;
request_pool_size           8k;

END;
		}

		if ($config->opttcpperf == 1)
		{
			$nginxConf .= <<<END
# -- Performance, see http://wiki.nginx.org/HttpCoreModule
sendfile on;
sendfile_max_chunk 1m;
postpone_output 0;
tcp_nopush on;
tcp_nodelay on;

END;
		}

		if ($config->optoutbuf == 1)
		{
			$nginxConf .= <<<END
# -- Output buffering, see http://wiki.nginx.org/HttpCoreModule
output_buffers 8 32k;

END;
		}

		if ($config->optfhndlcache == 1)
		{
			$nginxConf .= <<<END
# -- Filehandle Cache, useful when serving a large number of static files (Joomla! sites do that)
open_file_cache max=2000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;

END;
		}

		if ($config->encutf8 == 1)
		{
			$nginxConf .= <<<END
# -- Character encoding, see http://wiki.nginx.org/HttpCharsetModule
charset                 utf-8;
source_charset          utf-8;

END;
		}

		if ($config->nginxsecurity == 1)
		{
			$nginxConf .= <<<END
# -- Security options, see http://wiki.nginx.org/HttpCoreModule
server_name_in_redirect off;
server_tokens off;
ignore_invalid_headers on;

END;
		}

		if ($config->maxclientbody == 1)
		{
			$nginxConf .= <<<END
# -- Maximum client body size set to 1 Gigabyte
client_max_body_size 1G;

END;
		}

		if ($config->blockcommon == 1)
		{
			$nginxConf .= <<<END
set \$common_exploit 0;
if (\$query_string ~ "proc/self/environ") {
	set \$common_exploit 1;
}
if (\$query_string ~ "mosConfig_[a-zA-Z_]{1,21}(=|\%3D)") {
	set \$common_exploit 1;
}
if (\$query_string ~ "base64_(en|de)code\(.*\)") {
	set \$common_exploit 1;
}
if (\$query_string ~ "(<|%3C).*script.*(>|%3E)") {
	set \$common_exploit 1;
}
if (\$query_string ~ "GLOBALS(=|\[|\%[0-9A-Z]{0,2})") {
	set \$common_exploit 1;
}
if (\$query_string ~ "_REQUEST(=|\[|\%[0-9A-Z]{0,2})") {
	set \$common_exploit 1;
}
if (\$common_exploit = 1) {
	return 403;
}

END;
		}

		if ($config->enablesef == 1)
		{
			$nginxConf .= <<<NGINX
## Enable SEF URLs

NGINX;
			$nginxConf .= <<<NGINX
# Joomla API application
location $rewritebaseSlash/api/ {
	try_files \$uri \$uri/ $rewritebaseSlash/api/index.php?\$args;
}

NGINX;

			if ($restrictIP === 'none')
			{
				$nginxConf .= <<<NGINX
# Joomla public frontend application
location $rewritebaseSlash/ {
	try_files \$uri \$uri/ $rewritebaseSlash/index.php?\$args;
}

NGINX;
			}

			$nginxConf .= <<<NGINX
# Parse index.php as a PHP executable file
location ~* ^$rewritebaseSlash/index.php$ {
	$fastcgi_pass_block
	break;
}

NGINX;
		}

		$nginxConf .= <<< END
######################################################################
## Advanced server protection rules exceptions
######################################################################

END;

		$hasBackendCsp = ($config->backendprot == 1) && ($config->bestaticrisks == 1);
		$hasFrontendCsp = ($config->frontendprot == 1) && ($config->festaticrisks == 1);
		$antiCsp = ($hasBackendCsp || $hasFrontendCsp) ? 'add_header Content-Security-Policy "default-src \'self\' \'unsafe-eval\' \'unsafe-inline\';";' : '';

		foreach ($config->exceptionfiles as $file)
		{
			if (substr($file, -4) == '.php')
			{
				$nginxConf .= <<<END
location = $rewritebaseSlash/$file {
	$antiCsp
	$fastcgi_pass_block
	break;
}

END;
				continue;
			}

			$nginxConf .= <<<END
location = $rewritebaseSlash/$file {
	$antiCsp
	break;
}

END;
		}

		foreach ($config->exceptiondirs as $dir)
		{
			$dir       = trim($dir, '/');
			$dir       = $this->escape_string_for_regex($dir);
			$nginxConf .= <<<END
location ~* ^$rewritebaseSlash/$dir/.*\.php$
{
	break;
}
location ~* ^$rewritebaseSlash/$dir/.*$
{
	$antiCsp
	break;
}

END;
		}

		foreach ($config->fullaccessdirs as $dir)
		{
			$dir       = trim($dir, '/');
			$dir       = $this->escape_string_for_regex($dir);
			$nginxConf .= <<<END
location ~* ^$rewritebaseSlash/$dir/.*$
{
	$antiCsp
	break;
}

END;
		}

		$nginxConf .= <<< END
######################################################################
## Advanced server protection
######################################################################

END;

		if ($config->backendprot == 1)
		{
			$directories = $this->bugfixBackendProtectionExclusionDirectories($config->bepexdirs ?: []);
			$bedirs      = implode('|', $directories);
			$betypes     = is_array($config->bepextypes) ? implode('|', $config->bepextypes) : $config->bepextypes;
			$csp         = ($config->bestaticrisks == 1) ? 'add_header Content-Security-Policy "default-src \'self\'; script-src \'none\';";' : '';
			$nginxConf   .= <<<END
# Allow media files in select back-end directories
location ~* ^$rewritebaseSlash/administrator/($bedirs)/.*.($betypes)$ {
	$csp
	break;
}

# Allow access to the back-end index.php file
location ~ ^$rewritebaseSlash/administrator/index.php(\?.*)?$ {
	$fastcgi_pass_block
	break;
}
location ~* ^$rewritebaseSlash/administrator$ {
	return 301 $rewritebaseSlash/administrator/index.php?\$args;
}
location ~* ^$rewritebaseSlash/administrator/$ {
	return 301 $rewritebaseSlash/administrator/index.php?\$args;
}

# Disable access to everything else.
location ~* ^$rewritebaseSlash/administrator.*$ {
	# If it is a file, directory or symlink and I haven't deliberately
	# enabled access to it, forbid any access to it!
	if (-e \$request_filename) {
		return 403;
	}
	# In any other case, just treat as a SEF URL
	try_files \$uri \$uri/ $rewritebaseSlash/administrator/index.php?\$args;
}

END;
		}

		if ($config->frontendprot == 1)
		{
			$fedirs    = is_array($config->fepexdirs) ? implode('|', $config->fepexdirs) : $config->fepexdirs;
			$fetypes   = is_array($config->fepextypes) ? implode('|', $config->fepextypes) : $config->fepextypes;
			$csp = ($config->festaticrisks == 1) ? 'add_header Content-Security-Policy "default-src \'self\'; script-src \'none\';";' : '';
			$nginxConf .= <<<END
# Allow limited access to additional TinyMCE plugins' HTML files
location ~* ^$rewritebaseSlash/media/plg_editors_tinymce/js/plugins/.*\.(htm|html)$ {
	$csp
	break;
}

# Allow media files in select front-end directories
location ~* ^$rewritebaseSlash/($fedirs)/.*.($fetypes)$ {
	$csp
	break;
}

## Disallow front-end access for certain Joomla! system directories (unless access to their files is allowed above)
location ~* ^$rewritebaseSlash/includes/js/ {
	return 403;
}
location ~* ^$rewritebaseSlash/(cache|includes|language|logs|log|tmp)/ {
	return 403;
}

END;
			if ($config->enablesef != 1)
			{
				$nginxConf .= <<<END
# Allow access to the front-end index.php file
location ~* ^$rewritebaseSlash/$ {
	return 301 $rewritebaseSlash/index.php?\$args;
}
location ^$rewritebaseSlash/index.php$ {
	$fastcgi_pass_block
	break;
}

END;
			}

			$nginxConf .= <<<END
# Allow access to /
location ~* ^$rewritebaseSlash/$ {
	return 301 $rewritebaseSlash/index.php?\$args;
}

# Disable access to everything else.
location ~* ^$rewritebaseSlash/.*$ {
	# If it is a file, directory or symlink and I haven't deliberately
	# enabled access to it, forbid any access to it!
	if (-e \$request_filename) {
		return 403;
	}
	# In any other case, just treat as a SEF URL
	try_files \$uri \$uri/ $rewritebaseSlash/index.php?\$args;
}

END;
		}

		$nginxConf .= "##### Advanced server protection -- END\n\n";

		return $nginxConf;
	}
}

© 2025 Cubjrnet7