0

I converted some mod_rewrite rules for nginx and they were working fine when I tested it locally but for some reason they are not working on my website.

Can anyone help where I'm going wrong?

Apache rules:

<filesMatch "\.(htm|html|css|js|php)$">

AddDefaultCharset UTF-8

DefaultLanguage en-US

</filesMatch>
<IfModule mod_rewrite.c>
ErrorDocument 404 /404.php
RewriteEngine On
RewriteRule ^article.*$ / [R=301,L]



RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*\.* /loadpage.php [QSA,L]

</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine On


RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*\.html /courses/index.php [L]

</IfModule>

Nginx converted rules:

 charset utf-8;
        error_page 404 /404.php;
location /article {
         rewrite ^/article.*$ / redirect;
        }

        location / {
         if (!-e $request_filename){
          rewrite ^/.*\.* /loadpage.php break;
          }
         }

          if (!-e $request_filename){
           rewrite ^/.*\.html /courses/index.php break;
          }

1 Answer 1

0

The problem

Your last rewrite rule is outside of any location block. Admittedly, I am not sure how nginx handles cases where there's both a rewrite rule inside a matching location block and inside server (I would assume it will always use the one inside server as that is what is evaluated first). What I am sure about however is that this leads to unexpected results.


The solution

First and foremost, if is evil. Do not use it unless there's absolutely no other way to achieve the same result (which really only happens in very obscure situations).

A better alternative to check the existence of a file is the try_files directive.

Checks the existence of files in the specified order and uses the first found file for request processing; the processing is performed in the current context. The path to a file is constructed from the file parameter according to the root and alias directives. It is possible to check directory’s existence by specifying a slash at the end of a name, e.g. “$uri/”. If none of the files were found, an internal redirect to the uri specified in the last parameter is made.

Your rewrite rules would then look like this:

charset utf-8;
error_page 404 /404.php;

location /article {
  rewrite ^/article.* / redirect;
}

location / {
  try_files $uri $uri/ /loadpage.php?$args;
}

The last rewrite rule, which is likely to be the problematic one, would look something like this with try_files inside its own location block:

location ~ \.html$ {
  try_files $uri /courses/index.php?$args;
}

The ~ modifier tells nginx that this location uses a regular expression. It matches all URI's that end with .html. This location block gets preference over the other location blocks because it is more specific (regular expression location blocks are examined before "normal" ones). More info on nginx location block selection.


Additional notes & resources

The $ after ^/article.* is redundant since you are matching all characters behind it anyway. Appending ?$args to the URI of the try_files directive ensures that all GET parameters are passed along to it.

13
  • Thanks for such a great piece of information. On applying your rules, my home page was being redirected to loadpage.php, so I added location /.* { try_files $uri /loadpage.php?$args; } My 1st rule and 3rd rule were working even before but it is the 2nd rule that is creating problems. What 2nd rule says is, everything after localhost/ that doesn't matches any file on the server should be redirected to loadpage.php for eg. localhost/dskjdnjsdn localhost/sjhdksjhd.jpg localhost/djkshdjksd.php or any extension
    – Axel
    Commented Apr 23, 2019 at 13:01
  • I hope you can understand my previous comment, it's a little messed up. 2nd rule is still not working I don't know why.
    – Axel
    Commented Apr 23, 2019 at 13:07
  • Ah, I see. Assuming you have an index defined, you could then probably just do this: location / { try_files $uri $uri/ /loadpage.php?$args; }. The second $uri/ will match the index when there's nothing behind the / of the URI.
    – Thomas
    Commented Apr 23, 2019 at 13:09
  • Thank you so much Thomas. It is working now, but localhost/anyname.php is still not working. It is being redirected to 404 instead of loadpage.php. All other extensions like localhost/anyname localhost/anyname.jpg localhost/anyname.phpp are being redirected successfully.
    – Axel
    Commented Apr 23, 2019 at 13:23
  • @Axel That is probably because you are catching \.php$ somewhere to redirect all requests ending in .php to PHP. Remember the location block selection algorithm: regular expressions are evaluated first, so what happens is that it sees localhost/anyname .php, which matches the \.php$ block, which in turns tries to pass this to the PHP parser without checking it first. A solution could be to add try_files $uri /loadpage.php?$args; to your \.php$ location block.
    – Thomas
    Commented Apr 23, 2019 at 13:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .