1

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.

2 Answers 2

2

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; } 
11
  • Thank you for the answer, Tested your solution, It is giving me 500 Internal error. and restricting access to data folder still the same, when I try to access .zip file, I still am able to access without being logged. I agree with using exact location of index.php is a safer option. trying try_files $uri $uri/ /index.php$uri; will work only for those rewrite locations, rest of the things will end up in 500 internal error instead of index.php. Commented Jan 15, 2020 at 15:12
  • You should run nginx -T to check the configuration and look into the error log: /var/log/nginx/error.log Commented Jan 15, 2020 at 16:25
  • testing under lab condition now. this is the error: rewrite or internal redirection cycle while internally redirecting to "/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/" ... And with, location ~ ^/(data|img)/ is same output as location /(data|img)/ or location ^~ /(data|img)/ they all block the folder access but deliver the file when requested. /data/backup.zip Commented Jan 15, 2020 at 17:19
  • Sorry, nothing replaces testing the solution. I went to far with location = /index.php, location /index.php/ in the edited solution works. You should comment all your own location blocks before testing. Something is overriding the deny all on the /data path. Commented Jan 15, 2020 at 17:58
  • Yes, agreed. I just did a clean server block based on your updated solution. and wow, I can see a lot of success with this now.rewrite location blocks are not necessary anymore, it does forward to index.php /p/all_articles/user/ABC or /p/all_articles/user/ABC/page/2 loads perfectly fine. Most of the links work as it suppose to be with clean URL except for search function, about_us and more importantly home or index page ends with 404, access log has access to /index.php/ 404 and no errors. And, /data path is fixed. It does deny everything from the folder. Commented Jan 15, 2020 at 18:42
0

Solution for rewrite

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

Notice, all I did was to interchange lines. Credits to Richard Smith


Thanks to Piotr P. Karwasz, for the the other solution, it might help someone whose script is 100% compatible to handle clean URL on it's own.

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; } 

the above solution is a way to go as long as your script work with clean URL 100%. Here, you do not need to put 100s of rewrite location blocks, and nginx will append the entire request URI to /index.php which is very interesting and helpful, probably this is the real solution, but in my case my script was not 100% compatible with this. Still this is a good, cleaver solution.


Soution for preventing folder, files access

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

Credits to Piotr P. Karwasz pointed out, the deny all was being overridden by something, upon clean server block it did solve the issue. Also make sure to use either deny all; or return 404; but not together.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.