Apache 2 rewrite rule is incorrectly changing the URL

烂漫一生 提交于 2019-12-11 07:10:25

问题


Note this is a repeat of a previous question I have asked. However it was initially badly worded and was edited so many times it almost became a different question, hence I decided to make a new question

Files

I have the following files:

example.dev.conf vhost file

This is a very simple vhost file:

<VirtualHost *:80>
        ServerName example.dev
        ServerAlias *.example.dev
        DocumentRoot /var/www/example

        <Directory /var/www/example >
                AllowOverride All
                Order allow,deny
                Allow from all
        </Directory>
</VirtualHost>

<VirtualHost *:443>
        ServerName example.dev
        ServerAlias *.example.dev 
        DocumentRoot /var/www/example

        <Directory /var/www/example >
                AllowOverride All
                Order allow,deny
                Allow from all
        </Directory>
</VirtualHost>

.htaccess

The following file redirects all urls to the https equivalent, unless it starts with /api/.

It should also makes sure that ALL routes uses the index.php controller.

RewriteEngine On

# Handle SSL
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} !^/api/ [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R]

# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

The Problem

This works for the most part.

For example http://example.dev/blah redirects to https://example.dev/blah

However, it does not seem to work as expected for anything that starts with /api/.

For example: http://example.dev/api/blah redirects to https://example.dev/index.php, when it should have not done a redirect at all!

Note that the redirects work fine if I remove the final 3 lines of the htaccess file.

How to replicate?

I have found that some people found it difficult to replicate my problem and have insisted there is some other rule somewhere. Hence I decided to show ALL my steps the best I can so it's very easy to replicate:

vagrant init ubuntu/trusty64
vagrant up --provider virtualbox
vagrant ssh

sudo apt-get install apache2
sudo a2enmod rewrite

cd /etc/apache2/sites-available/
sudo touch example.dev.conf   
# copy contents of the example.dev.conf vhost file as seen above
sudo a2ensite example.dev.conf

cd /var/www/
sudo mkdir example
sudo chown -R www-data:www-data /var/www/example
sudo chmod 777 -R /var/www/example
cd example
echo "test" > index.php
touch .htaccess
# Copy contents of .htaccess file as seen above

sudo chown -R www-data:www-data /var/www/example
sudo chmod 777 -R /var/www/example

sudo service apache2 restart

回答1:


Both other answers are good but I am posting another for these 3 reasons:

  1. Answer from Olaf: It will work for all the cases except when user directly enters http://domain.con/index.php in the browser. As per requirements anything except when starting with /api/ should be redirected to `https://
  2. Answer from Yahya: END flag is only supported in Apache 2.4+ so this answer won't work for someone on Apache 2.2 facing similar answer.
  3. POST requests should be redirected with 307/308 otherwise post data will be lost (See Olaf's helpful comment below).

To make it work for both these cases I suggest using THE_REQUEST instead of REQUEST_URI because REQUEST_URI changes to /index.php after last rule execution.

RewriteEngine On

# Handle SSL
RewriteCond %{HTTPS} off
RewriteCond %{THE_REQUEST} !\s/+api/ [NC]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=308,NE]

# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]



回答2:


Most likely, the reason for /api/ requests being redirected to https://example.dev/index.php are the following steps

  • rewrite /api/... to index.php
  • redirect index.php to https://...

To see why this happens, look at how Ruleset Processing works. The rules are tried in order, but when a rewrite occurs ("URI changed?" in the picture), Apache starts with the resulting URI again, until no further rewrite occurs.


To avoid the redirect to https://... for /api/, you must also exclude index.php for the first rule, e.g.

RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} !^/api/ [NC]
RewriteCond %{REQUEST_URI} !^/index\.php$ [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R]



回答3:


I think I managed to figure out the issue.

Upon reading this: http://httpd.apache.org/docs/current/rewrite/flags.html#flag_l it turns out the last flag doesn't necessarily mean no further rules are used.

For example, for the following file:

RewriteEngine On

# Handle SSL
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} !^/api/ [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=308]

# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
  • /api/blah is rewritten to index.php
  • It then goes through all the rules again. According to the documentation, this can happen for the following reasons:

It is possible that as the rewritten request is handled, the .htaccess file or section may be encountered again, and thus the ruleset may be run again from the start. Most commonly this will happen if one of the rules causes a redirect - either internal or external - causing the request process to start over.

  • index.php then matches the first rule so a redirect to https://example.dev/index.php is created.

The solution is to instead use the END flag (which as @anubhava mentioned, is only supported in Apache 2.4+), to prevent the rules being re-processed.

So the solution is as follows:

RewriteEngine On

# Handle SSL
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} !^/api/ [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R]

# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [END]

Now when I go to http://example.dev/api/blah:

  • It is rewritten to index.php
  • As it has the END rule, the rules are not processed again!
  • Therefore http://example.dev/api/blah stays where it is and uses the index.php controller as desired.

Thanks @Olaf Dietsche for getting me to the right track and suggesting the 308 redirect!



来源:https://stackoverflow.com/questions/42695014/apache-2-rewrite-rule-is-incorrectly-changing-the-url

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!