Nginx – How to Nginx rewrite to get clean url of page numbers

.htaccessnginxngx-http-rewrite-modulerewriteurl

Recently we have moved to Nginx from Apache,
Under apache it used to be very easy just put some stuff on .htaccess and done.

RewriteEngine on
RewriteBase /

# only rewrite if the requested file doesn't exist
RewriteCond %{REQUEST_FILENAME} !-s

# pass the rest of the request into index.php to handle
RewriteRule ^(.*)$ /index.php/$1 [L]

The above served great to clean URL and let index.php handle all requests.
but in Nginx we needed to rewrite every unique URL in location block. However this is not 'automatic' like apache.

Few examples of our rewrite location block

location / {
try_files $uri $uri/ /index.php;
}

location /p {
rewrite ^/p(?:/([a-z_]+))?$ /index.php?p=$1 last;
rewrite ^/p/all_articles/user/(.*)?$ /index.php?p=all_articles&user=$1 last;
try_files $uri $uri/ /index.php;
}

location /about_us {
rewrite ^/about_us /index.php?about_us last;
try_files $uri $uri/ /index.php;
}

location /search {
rewrite ^/search/(.*) /index.php?search=$1;
rewrite ^/search/(.*)/page/(.*)?$ /index.php?search=$1&page=$2 last;
try_files $uri $uri/ /index.php;
}

location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}

the above does good job in clean URL, but when we need to get pages for example

/p/all_articles/user/ABC/page/2

/index.php?p=all_articles&user=ABC&page=2

we have tried

rewrite ^/p/all_articles/user/(.*)/pg(?:/([0-9]+))?$ /index.php?p=all_articles&user=$1&pg=$2 last;

this only works when we place in separate location block

location /page/all_articles {
rewrite ^/p/all_articles/user/(.*)/pg(?:/([0-9]+))?$ /index.php?p=all_articles&user=$1&pg=$2 last;
try_files $uri $uri/ /index.php;
}

and when done so, it would not let

/p/all_articles/user/ABC

to load.

also, search result pages would not work at all.


another issue we came across is on folder
.htaccess

Order deny,allow
Deny from all
Options -Indexes

Under apache this would prevent any access to that folder and files except for php script.
We tried,

location /(data|img)/ {
   deny all;
   return 404;
}

It does block the access to folder but, if you specify the filename, it will still serve, without denying access for example;

/data/backup_01012020.zip under apache .htaccess, only certain users were allowed to access this, while logged. and outside of it, apache will deny any access. But under nginx eventhough it gives 404 when trying to access /data/. Even when you are not logged, it would serve backup_01012020.zip file right away.

Now we can not figure out what we can do, which used to be piece of cake with apache. Our application is based on PHP and index.php is capable of handling all clean URL requests. It could have been great if Nginx simply pass all requests to index and let it handle instead of plenty of rewrites and location blocks. Any help would be great.

Best Answer

You might be interested in the questions with the rewrite tag, since it contains many variations of your problem.

Your Apache rewrite rule:

RewriteRule ^(.*)$ /index.php/$1 [L]

appends the entire request URI to /index.php. In nginx the path of the URI (normalized) is available in the $uri variable. If you need the query arguments too, you can use the $request_uri instead.

A strict translation of your rewrite rules would therefore be:

location / {
    # Size zero static files are served.
    # I don't believe that is an issue.
    try_files $uri /index.php$request_uri;
}
# If no other .php files are accessible a prefix location of '/index.php/'
# is safer.
location /index.php/ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    # Probably duplicates the contents of fastcgi-php.conf
    # fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    # include fastcgi_params;
}

Your deny directive on the /(data|img)/ location does not work, since you are using a prefix match, instead of a regex match:

location ~ ^/(data|img)/ {
   # Only one is required
   deny all;
   # return 404;
}
Related Topic