0

With some help from @jon-lin, we figured out how to remove trailing slashes on Apache (Vanity URLs without trailing slashes on Apache); now, as we plan to run our site on Nginx, we'd like to do the same. We tried several suggestions we found on stackoverflow and elsewhere, but all result in an infinite loop (probably because browsers try to put the slashes back in). Our current configuration (with three of our attempts -- rewrite ^/(.*)/$ https://www.example.com/$1 permanent; rewrite ^/(.*)/$ https://www.example.com/$1; and rewrite ^/(.*)/$ /$1 permanent; -- commented out) is:

server {
        listen 80;
        server_name  example.com;
        return 301 https://www.example.com$request_uri;
}
server {
        listen 80;
        server_name  www.example.com;
        return 301 https://www.example.com$request_uri;
}
server {
        listen 443;
        server_name  example.com;
        return 301 https://www.example.com$request_uri;
}

server {
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;
        server_name www.example.com;

        #rewrite ^/(.*)/$ https://www.example.com/$1 permanent;
        #rewrite ^/(.*)/$ https://www.example.com/$1;
        #rewrite ^/(.*)/$ /$1 permanent;

        root /var/www/example.com/html;
        index index.php index.html index.htm;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/var/run/php5-fpm.sock;
        }

        ssl on;
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECD$
        add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
        ssl_dhparam /etc/ssl/certs/dhparam.pem;

}

How can this be done?

In our particular case, we needed all URLs to be stripped of their trailing slashes; we accomplished that by adding the following to Apache's httpd.conf:

DirectorySlash Off

RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*[^/])$ /$1/

Then, rewriting all entries in /profiles/ was accomplished with this:

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /profiles/$1 [NC,L]
haadaa
  • 87
  • 11

2 Answers2

1

If your no longer use the /profiles/ directory, this should work:

server {
  ...
  root /var/www/example.com/html;
  index index.php index.html index.htm;

  location / {
    try_files $uri @rewrite;
  }
  location @rewrite {   
    rewrite ^(.*[^/])$ $1/; 
  }
  location ~ \.php$ { ... }
}

In the above configuration we avoid the $uri/ element on the try_files as it triggers a redirect that adds a trailing / to the current URL.

If you want a configuration similar to your previous Apache configuration, then this should work:

server {
  ...
  root /var/www/example.com/html;
  index index.php index.html index.htm;

  location / {
    try_files $uri $uri/ @rewrite;
  }
  location @rewrite {   
    rewrite ^(.*[^/])$ /profiles$1/; 
  }
  location ~ \.php$ { ... }
}

As you can see, the $uri/ element does no harm in this situation.

Following your comments and recent edit, this third (less elegant) configuration combines features from the other two, and is a close (but not exact) implementation of your Apache configuration:

server {
  ...
  root /var/www/example.com/html;
  index index.php index.html index.htm;

  location = / { rewrite ^ /index.php; }
  location / {
    try_files $uri @rewrite;
  }
  location @rewrite {
    if (-d $document_root/profile$uri) { rewrite ^ /profiles$uri/ last; }
    if (-d $request_filename) { rewrite ^(.*[^/])$ $uri/ last; }
  }
  location ~ \.php$ { ... }
}

And finally, this version deviates slightly from the brief. The php execution block is replaced by a named location. But is does allow the use of cascaded try_files directives, removing the nasty if directives, and discarding index in the process. Enjoy:

server {
  ...
  root /var/www/example.com/html;

  location / {
    try_files $uri $uri.html $uri/index.html /profiles$uri/index.html @php;
  }
  location @php {
    try_files $uri.php $uri/index.php /profiles$uri/index.php =404;
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
  }
  location ~ \.php$ { rewrite ^(.*)\.php$ $1 permanent; }
}

It's a hybrid of slash-less and extension-less php, with your profiles tweak.

Richard Smith
  • 45,711
  • 6
  • 82
  • 81
  • Thank you, @richard-smith! Your first option (without `/profiles/`), worked perfectly! The second one didn't -- that is, Nginx refused to restart if we left `$url/` in there for some reason. But the first option works for us, and so you really solved it! Thanks again! – haadaa Dec 06 '15 at 00:19
  • Hey @richard-smith -- it appears `$url/` was a typo -- when we changed that to `$uri/`, your second option also worked. Which actually is very cool; is there a way to combine both so there are no trailing slashes anywhere? – haadaa Dec 06 '15 at 02:57
  • @haadaa Not sure what you mean by combine both. Option #1 already works at any level in the hierarchy. Fixed url typo, thanks. – Richard Smith Dec 06 '15 at 08:50
  • Hey @richard-smith! Your second option shortens the urls in the `/profiles/` directory and also removes the trailing slashes -- a profile at `example.com/profiles/profile1/index.php` becomes `example.com/profile1` -- exactly what we want. But we'd also like to remove trailing slashes elsewhere on the site. Right now, when we opt for your second option, `example.com/noslash/` works while `example.com/noslash` returns a 404. We'd like example.com/noslash to work as well. (If, however, we opt for your first option, `example.com/noslash` works, but `example.com/profile1` returns a 404.) – haadaa Dec 06 '15 at 09:15
  • @haadaa The problem is that `example.com/noslash` is being redirected to `example.com/profiles/noslash/index.php` which doesn't exist. What would you want it to do? Check for the existence of `example.com/noslash.php` and `example.com/noslash/index.php` first before rewriting? It's doable, but I might need to think about it for a bit :-) – Richard Smith Dec 06 '15 at 10:29
  • Hey @richard-smith! Yeah, that shouldn't be happening -- only subdirectories of `/profiles/` should be rewritten. All other pages should be untouched, except they should also be stripped of their trailing slashes. I'll try to edit the question and add the code we used on Apache. – haadaa Dec 06 '15 at 13:25
  • Hey @richard-smith! I added the code we've been using in Apache to our question above. Basically, your first option is perfect for us -- it strips everything on our site of their trailing slashes -- exactly what we need. We then (in addition or simultaneously) also need to rewrite the subdirectories of `profiles`. Your second option does this beautifully as well, but in this case, all the remaining pages keep the slashes that we need to remove. – haadaa Dec 06 '15 at 13:54
  • @haadaa updated answer with third option. I've tested it a little, but you will want to test it more thoroughly. – Richard Smith Dec 06 '15 at 17:18
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/97139/discussion-between-haadaa-and-richard-smith). – haadaa Dec 06 '15 at 21:18
  • Hey @richard-smith! Let's continue talking about this at https://chat.stackoverflow.com/rooms/97139/discussion-between-haadaa-and-richard-smith – haadaa Dec 07 '15 at 05:31
0

Combining @richard-smith's first option in his answer here with the "evil" if, we made it work:

    location / {
        if (!-e $request_filename) {
        rewrite ^(.*)$ /profiles$1/ break;
        }
        try_files $uri @rewrite;
    }

    location @rewrite {
        rewrite ^(.*[^/])$ $1/;
    }

Now all trailing slashes are gone and profiles in example.com/profiles/profile/ are rewritten to example.com/profile

haadaa
  • 87
  • 11