I'm running Magento on Nginx using this config: http://www.magentocommerce.com/wiki/1_-_installation_and_configuration/configuring_nginx_for_magento.

Now I want to 301 all URLs without trailing slash to their counterpart that includes a trailing slash. For example: /contacts to /contacts/.

I've tried virtually all the nginx directives on this I could find, but to no avail. For example, the directive specified in nginx- Rewrite URL with Trailing Slash leads to a redirect to /index.php/.

Which directive should I add and where?

2 Answers 2


I've found the solution: I added the following line above the "try_files" directive in the "location /"-block:

rewrite ^([^.]*[^/])$ $1/ permanent;

which does the magic.

  • Could you go into detail on the behavior of the regex?
    – DanCat
    Jul 26, 2017 at 19:23
  • @DanCat Something like "group of any number of non-period characters not followed by a slash"
    – helb
    Oct 10, 2017 at 16:59

This is very, very tricky because you have to consider all possibilities in your URLs. Let's have a closer look at that configuration you posted there and optimize it while trying to implement your wish. I have to correct the complete configuration because it contains more than one security risk for your website (and continue reading after the configuration).

server {
    server_name    DOMAIN.com;
    return         301 $scheme://www.$server_name$request_uri;

server {
    index          index.html index.php;
    listen         80 default;
    root           /var/www;
    server_name    www.DOMAIN.com;

    location / {

        # Hide ALL kind of hidden stuff.
        location ~ /\. {
            return 403;

        # Protect Magento's special directories in document root.
        location ~* ^/(app|includes|lib|media/downloadable|pkginfo|report/config\.xml|var)/? {
            return 403;

        # Directly deliver known file types.
        location ~* \.(css|gif|ico|jpe?g|js(on)?|png|svg|webp)$ {
            access_log      off;
            add_header      Cache-Control   "public";
            add_header      Pragma          "public";
            expires         30d;
            log_not_found   off;
            tcp_nodelay     off;
            try_files       $uri =404;

        # Do not allow direct access to index.php
        location ~* ^(.*)index\.php$ {
            return 301 $1;

        # Extremely risky ... oh boy!
        location ~* \.php/ {
            rewrite ^(.*\.php)/ $1 last;

        # Not direct index.php access and not one of those ultra
        # risky php files with a path appended to their script name,
        # let's try to add a slash if it's missing.
        location ~* ^(.*)[^/]+$ {
            return 301 $1/;

        location ~* \.php$ {
          include          fastcgi_params;
          fastcgi_index    index.php;
          fastcgi_param    PATH_INFO          $fastcgi_path_info;
          fastcgi_param    PATH_TRANSLATED    $document_root$fastcgi_path_info;
          fastcgi_param    SCRIPT_NAME        $fastcgi_script_name;
          fastcgi_param    SCRIPT_FILENAME    $document_root$fastcgi_script_name;
          fastcgi_param    MAGE_RUN_CODE      "default";
          fastcgi_param    MAGE_RUN_TYPE      "store";

          # Ensure it's an actual PHP file!
          try_files        $uri =404;

    location ^~ /var/export/ {
        auth_basic              "Restricted";
        auth_basic_user_file    htpasswd;
        autoindex               on;


I can't test this configuration, I've written it down to my best knowledge. Please execute nginx -t before attempting to reload your nginx and report back if this reports any errors. Do not, I repeat, do not test this on your production site and test everything you can think of.

  • Thank you for your answer, but that doesn't solve my problem.
    – vincent.io
    Oct 30, 2013 at 8:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.