• 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 Nginx URL re-writing dilemma

OlgaKM

Basic Pleskian
I'm having an issue with Nginx and URL rewriting. By default, the Plesk generated nginx.conf file for each vhost contains the following block:

Code:
    location ~ /$ {
        index index.html index.cgi index.pl index.php index.xhtml index.htm index.shtml;
    }

I have a situation where when I receive any requests to http://www.example.com/cat(whatever), they need to be re-written to http://www.example.com/index.php?cat=(whatever). So I added the following block using 'Additional nginx directives':

Code:
location /cat {
    try_files $uri /index.php?cat=$uri;
}

Now if I try to request any of the following URLs, this works exactly as expected:

Code:
http://www.example.com/cat-test
http://www.example.com/cat/test
http://www.example.com/cat/test/test
http://www.example.com/cat/t

But as soon as I add the trailing slash, I start getting a 404 error:

Code:
http://www.example.com/cat/
http://www.example.com/cat/test/
http://www.example.com/cat/test/test/
http://www.example.com/cat/t/

The error log tells that this is because corresponding index.html files cannot be found. Here is one example:

Code:
2017/03/21 00:32:49 [error] 25711#0: *20566477 "/var/www/vhosts/example.com/httpdocs/cat/test/index.html" is not found (2: No such file or directory), client: <ip redacted>, server: example.com, request: "GET /cat/testing/ HTTP/1.1", host: "www.example.com"

So it looks like as soon as nginx hits a URL that matches the 'location ~ /$', it abandons looking at the other location blocks. So I have 2 questions here:
1. According to the nginx docs, prefix matches (i.e. location /cat) should be processed BEFORE any regex matches (i.e. location ~ /$). So this is unexpected behavior. Shouldn't nginx look at location /cat first?
2. Commenting out the 'location ~ /$' block in the nginx.conf file fixes the problem. However, this file is automatically generated by Plesk. Plus, I don't want to disable it for all requests, only requests directed to 'http://www.example.com/cat(whatever)'. How can I override this behavior in my 'Additional nginx directives'?

Your help is much appreciated.
 
Additionally, the automatically generated nginx.conf file does NOT seem to follow the guidelines laid out on the nginx "Pitfalls and Common Mistakes" page (!) https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/

I've discovered at least 2 issues:
1. The bizarre 'location ~ /$' block that is the cause of my issues. I'm no nginx guru, but wouldn't it be enough to simply place the line 'index index.html index.cgi index.pl index.php index.xhtml index.htm index.shtml;' somewhere within the 'server' block?
2. Use of if statement to redirect non-www traffic to www. The proper solution, according to nginx, is to set up several 'server' blocks with 301 redirects within them. This is a performance issue!

Do these problems get solved in Onyx? Maybe I should just look at upgrading.
 
Ultimately, and with the help of a thread on StackOverflow (How can I setup default directives for all servers in nginx? (Issues with Plesk)), I ended up modifying the default nginx vhost settings. I copied the following files:

Code:
/usr/local/psa/admin/conf/templates/default/domain/nginxDomainVirtualHost.php
/usr/local/psa/admin/conf/templates/default/domain/service/nginxSeoSafeRedirects.php

to

Code:
/usr/local/psa/admin/conf/templates/custom/domain/nginxDomainVirtualHost.php
/usr/local/psa/admin/conf/templates/custom/domain/service/nginxSeoSafeRedirects.php

Here is the new contents of my nginxDomainVirtualHost.php:

Code:
<?php
/**
 * @var Template_VariableAccessor $VAR
 * @var array $OPT
 */
?>

<?php echo $VAR->includeTemplate('domain/service/nginxSeoSafeRedirects.php', array('ssl' => $OPT['ssl'], 'frontendPort' => $OPT['frontendPort'])); ?>

server {
    listen <?php echo $OPT['ipAddress']->escapedAddress . ':' . $OPT['frontendPort'] .
        ($OPT['default'] ? ' default_server' : '') . ($OPT['ssl'] ? ' ssl' : '') .
        ($OPT['ssl'] && $VAR->domain->physicalHosting->proxySettings['nginxHttp2'] ? ' http2' : '') ?>;

<?php if ($VAR->domain->isWildcard): ?>
    server_name ~^<?php echo $VAR->domain->pcreName ?>$;
<?php else: ?>
<?php   if ($VAR->domain->isSeoRedirectToLanding) : ?>
    server_name <?php echo $VAR->domain->asciiName ?>;
<?php   elseif ($VAR->domain->isSeoRedirectToWww): ?>
    server_name www.<?php echo $VAR->domain->asciiName ?>;
<?php   else: ?>
    server_name <?php echo $VAR->domain->asciiName ?>;
    server_name www.<?php echo $VAR->domain->asciiName ?>;
<?php   endif; ?>
<?php   if ($OPT['ipAddress']->isIpV6()): ?>
    server_name ipv6.<?php echo $VAR->domain->asciiName ?>;
<?php   else: ?>
    server_name ipv4.<?php echo $VAR->domain->asciiName ?>;
<?php   endif ?>
<?php endif ?>
<?php if ($VAR->domain->previewDomainName): ?>
    server_name "<?php echo $VAR->domain->previewDomainName ?>";
<?php endif ?>

<?php if ($OPT['ssl']): ?>
<?php $sslCertificate = $VAR->server->sni && $VAR->domain->physicalHosting->sslCertificate ?
    $VAR->domain->physicalHosting->sslCertificate :
    $OPT['ipAddress']->sslCertificate; ?>
<?php   if ($sslCertificate->ce): ?>
    ssl_certificate             <?php echo $sslCertificate->ceFilePath ?>;
    ssl_certificate_key         <?php echo $sslCertificate->ceFilePath ?>;
<?php       if ($sslCertificate->ca): ?>
    ssl_client_certificate      <?php echo $sslCertificate->caFilePath ?>;
<?php       endif ?>
<?php   endif ?>
<?php endif ?>

<?php if (!empty($VAR->domain->physicalHosting->proxySettings['nginxClientMaxBodySize'])): ?>
    client_max_body_size <?php echo $VAR->domain->physicalHosting->proxySettings['nginxClientMaxBodySize'] ?>;
<?php endif; ?>

<?php if ($VAR->domain->physicalHosting->scriptTimeout): ?>
    proxy_read_timeout <?php echo min($VAR->domain->physicalHosting->scriptTimeout, 2147483); ?>;
<?php endif; ?>

    root "<?php echo $OPT['ssl'] ? $VAR->domain->physicalHosting->httpsDir : $VAR->domain->physicalHosting->httpDir ?>";
    access_log "<?php echo $VAR->domain->physicalHosting->logsDir . '/' . ($OPT['ssl'] ? 'proxy_access_ssl_log' : 'proxy_access_log') ?>";
    error_log "<?php echo $VAR->domain->physicalHosting->logsDir . '/proxy_error_log' ?>";

<?php if ($OPT['default']): ?>
<?php echo $VAR->includeTemplate('service/nginxSitePreview.php') ?>
<?php endif ?>

<?php echo $VAR->domain->physicalHosting->proxySettings['allowDeny'] ?>

    location / {
<?php echo $VAR->includeTemplate('domain/service/proxy.php', $OPT); ?>
    }

<?php if (!$VAR->domain->physicalHosting->proxySettings['nginxTransparentMode'] && !$VAR->domain->physicalHosting->proxySettings['nginxServeStatic']): ?>
    location /internal-nginx-static-location/ {
        alias <?php echo $OPT['documentRoot'] ?>/;
        add_header X-Powered-By PleskLin;
        internal;
    }
<?php endif ?>

<?php if ($VAR->domain->active && !$VAR->domain->physicalHosting->proxySettings['nginxTransparentMode']): ?>

<?php if ($VAR->domain->physicalHosting->php && $VAR->domain->physicalHosting->proxySettings['nginxServePhp']
            || $VAR->domain->physicalHosting->proxySettings['nginxServeStatic']): ?>

<?php if ($VAR->domain->physicalHosting->proxySettings['fileSharingPrefix']): ?>
    location ~ ^/<?php echo $VAR->domain->physicalHosting->proxySettings['fileSharingPrefix'] ?>/ {
<?php echo $VAR->includeTemplate('domain/service/proxy.php', $OPT); ?>
    }
<?php endif; ?>

<?php endif; ?>

<?php if ($VAR->domain->physicalHosting->proxySettings['nginxServeStatic']): ?>

    location @fallback {
<?php echo $VAR->includeTemplate('domain/service/proxy.php', $OPT); ?>
    }

<?php echo $VAR->includeTemplate('domain/service/nginxProtectedDirectories.php', $OPT); ?>

    location ~ ^/(.*\.(<?php echo $VAR->domain->physicalHosting->proxySettings['nginxStaticExtensions'] ?>))$ {
        try_files $uri @fallback;
    }
<?php endif ?>

<?php if ($VAR->domain->physicalHosting->php && $VAR->domain->physicalHosting->proxySettings['nginxServePhp']): ?>

    <?php echo $VAR->domain->physicalHosting->proxySettings['directoryIndex'] ?>

<?php if ($VAR->domain->physicalHosting->hasWebstat): ?>
    location ~ ^/(plesk-stat|webstat|webstat-ssl|ftpstat|anon_ftpstat|awstats-icon) {
        <?php echo $VAR->includeTemplate('domain/service/proxy.php', $OPT); ?>
    }
<?php endif; ?>

    location ~ ^/~(.+?)(/.*?\.php)(/.*)?$ {
        alias <?php echo $VAR->domain->physicalHosting->webUsersDir ?>/$1/$2;
        <?php echo $VAR->includeTemplate('domain/service/fpm.php'); ?>
    }

    location ~ ^/~(.+?)(/.*)?$ {
        <?php echo $VAR->includeTemplate('domain/service/proxy.php', $OPT); ?>
    }

    <?php echo $VAR->includeTemplate('domain/service/nginxWordpress.php', $OPT); ?>

    location ~ \.php(/.*)?$ {
        <?php echo $VAR->includeTemplate('domain/service/fpm.php'); ?>
    }

    <?php echo $VAR->includeTemplate('domain/service/nginxWordpressIndexing.php', $OPT); ?>

<?php endif ?>

<?php endif ?>

<?php if (is_file($VAR->domain->physicalHosting->customNginxConfigFile)): ?>
    include "<?php echo $VAR->domain->physicalHosting->customNginxConfigFile ?>";
<?php endif; ?>
}

Here is the new contents of my nginxSeoSafeRedirects.php:

Code:
<?php if ($VAR->domain->isSeoRedirectToLanding) : ?>
server {
    <?php echo $OPT['ssl'] ? 'listen '.$OPT['frontendPort'].";\n" : ''; ?>
    server_name www.<?php echo $VAR->domain->asciiName ?>;
    return 301 <?php echo $OPT['ssl'] ? 'https' : 'http'; ?>://<?php echo $VAR->domain->asciiName ?>$request_uri;
}
<?php elseif ($VAR->domain->isSeoRedirectToWww): ?>
server {
    <?php echo $OPT['ssl'] ? 'listen '.$OPT['frontendPort'].";\n" : ''; ?>
    server_name <?php echo $VAR->domain->asciiName ?>;
    return 301 <?php echo $OPT['ssl'] ? 'https' : 'http'; ?>://www.<?php echo $VAR->domain->asciiName ?>$request_uri;
}
<?php endif; ?>
<?php if ($VAR->domain->isAliasRedirected): ?>
<?php     foreach ($VAR->domain->webAliases AS $alias): ?>
<?php         if ($alias->isSeoRedirect) : ?>
server {
    <?php echo $OPT['ssl'] ? 'listen '.$OPT['frontendPort'].";\n" : ''; ?>
    server_name <?php echo $alias->asciiName ?>;
    return 301 <?php echo $OPT['ssl'] ? 'https' : 'http'; ?>://<?php echo $VAR->domain->targetName ?>$request_uri;
}
server {
    <?php echo $OPT['ssl'] ? 'listen '.$OPT['frontendPort'].";\n" : ''; ?>
    server_name www.<?php echo $alias->asciiName ?>;
    return 301 <?php echo $OPT['ssl'] ? 'https' : 'http'; ?>://<?php echo $VAR->domain->targetName ?>$request_uri;
}
<?php         endif; ?>
<?php     endforeach; ?>
<?php endif; ?>

Hopefully, this helps someone!
 
Last edited:
Back
Top