4

I got an old project and it has some bizarre issue. Its CodeIgniter project (version 2.X).

Google is indexing our URLs with index.php?. Below is an example

https://www.website.com/index.php?/my-seo-friendly-uri

I can see my pages from above url and with 2 more variants as below

https://www.website.com/index.php/my-seo-friendly-uri

https://www.website.com/my-seo-friendly-uri

We have used https://www.website.com/my-seo-friendly-uri throughout the site.

My question is how I can redirect https://www.website.com/index.php?/my-seo-friendly-uri and https://www.website.com/index.php/my-seo-friendly-uri to https://www.website.com/my-seo-friendly-uri?

The thing I already did

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^index.php/(.*)$ /$1 [R=302,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?/$1 [L] 
</IfModule>

This redirects index.php to normal URLs, but index.php? version of url is still not redirecting

In my config.php, I have the following settings

$config['index_page'] = '';
$config['uri_protocol'] = 'REQUEST_URI';
Shawn
  • 1,232
  • 1
  • 14
  • 44
sanjay ojha
  • 479
  • 5
  • 19

2 Answers2

2
https://www.example.com/index.php?/my-seo-friendly-uri

This URL contains a query string and so requires a slightly different rule in order to match it. The RewriteRule pattern matches against the URL-path only (just index.php in this case). The query string is available in its own variable.

Add the following, before your existing directives (in addition to the directive that matches /index.php/my-seo-friendly-url - which is passed as path-info):

# Redirect URLs of the form "/index.php?/my-seo-friendly-uri"
# And "/?/my-seo-friendly-uri"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^(/.*)
RewriteRule ^(index\.php)?$ %1 [QSD,R=302,L]

The query string is captured (2nd condition), and the backreference (%1) is used to construct the redirect.

The first condition that checks against the REDIRECT_STATUS environment variable is required in order to prevent a redirect loop, since you appear to be using the query string method to route the codeigniter URLs in the later rewrite. The REDIRECT_STATUS env var is empty on the initial request, but set to "200" (as in 200 OK HTTP status) after the first successful rewrite.

The QSD flag (Apache 2.4+) is required to discard the original query string from the redirected request. If you are still on Apache 2.2 then append a ? (empty query string) to the substitution string instead. ie. %1?

By making the match for index.php optional (ie. ^(index\.php)?$) it will also canonicalise URLs that omit index.php, but still include the query string (that may or may not currently be a problem). eg. /?/my-seo-friendly-uri.

Note that this is currently a 302 (temporary) redirect (as is your existing redirect). Only change this to a 301 (permanent) redirect once you have confirmed it works OK. 301s are cached persistently by the browser so can make testing problematic.

Summary

Your .htaccess file should look like this:

RewriteEngine On

# Query string...
# Redirect URLs of the form "/index.php?/my-seo-friendly-uri"
# And "/?/my-seo-friendly-uri"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^(/.*)
RewriteRule ^(index\.php)?$ %1 [QSD,R=302,L]

# Path-Info...
# Redirect URLs of the form "/index.php/my-seo-friendly-uri"
# Also handles "/index.php" only
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^index.php(?:/(.*))?$ /$1 [R=302,L]

# CodeIgniter Front-controller
# (NB: Using query string method to pass the URL)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) index.php?/$1 [L]

Additional notes...

  • The <IfModule> wrapper is not required.
  • (.*) is the same as ^(.*)$ since regex is greedy by default.
  • I've modified your existing path-info redirect (ie. /index.php/foo) to also redirect requests for /index.php only. This now requires an additional condition to avoid a redirect loop.
  • Your CodeIgniter front-controller is using the query string method to pass /my-seo-friendly-url to the backend (as used in the question). However, you have set $config['uri_protocol'] = 'REQUEST_URI'; - which contradicts with this (although shouldn't necessarily be a problem). However, if you are using the REQUEST_URI method then you can remove the ?/$1 part from the end of the final RewriteRule substitution string. For example:

    For example, from this:

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

    To this:

    RewriteRule . index.php [L]
    
MrWhite
  • 43,179
  • 8
  • 60
  • 84
  • Thank you @MrWhite, it is redirecting like this `https://www.website.com/index.php?/my-seo-friendly-uri` -> `https://www.website.com/my-seo-friendly-uri?/my-seo-friendly-uri` – sanjay ojha Feb 12 '20 at 07:37
  • 1
    Sorry, you need the `QSD` flag on the `RewriteRule` to discard the original query string from the target URL. I've updated my answer. – MrWhite Feb 12 '20 at 07:39
  • Now it works fot `index.php?` but not for `index.php` – sanjay ojha Feb 12 '20 at 07:57
  • 1
    "but not for `index.php`" - By that do you mean `/index.php` by itself and no path-info? I've modified your existing redirect to handle that scenario. What I've posted above should be all that's required - in addition to the directives you posted in the question (which was already redirecting URLs of the form `/index.php/foo`). I'm assuming you have no other directives in your `.htaccess` file that might otherwise be conflicting. I've updated my answer with a complete summary of exactly what your `.htaccess` file should look like. – MrWhite Feb 13 '20 at 19:10
  • 1
    Awesome, the last update did the trick. It is now working for both. I was not giving proper attention to identify both cases. I guess I need to learn lot. Also, this is great in depth explanation. Again, thank you very much – sanjay ojha Feb 14 '20 at 11:34
-1

Use .htaccess................

 <IfModule mod_rewrite.c>
 RewriteEngine on
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d

 <IfModule mod_php5.c>
     RewriteRule ^(.*)$ index.php/$1 [L]
 </IfModule>

 <IfModule !mod_php5.c>
     RewriteRule ^(.*)$ index.php?/$1 [L]
 </IfModule>

</IfModule>
ajimsha
  • 19
  • 1
  • 4
  • Working for `index.php?` but not for `index.php` – sanjay ojha Feb 12 '20 at 07:38
  • @sanjayojha The code above wouldn't "work for `index.php?`" (ie. it wouldn't canonicalise a request for `/index.php?/my-seo-friendly-url`) - it doesn't contain a redirect at all?! – MrWhite Feb 13 '20 at 19:18
  • I'm sorry, but this doesn't answer the question. It makes no attempt to _remove_ `index.php` from the requested URL - in order to canonicalise incorrectly indexed URLs. The code here is simply a CodeIgniter front-controller (pretty much the same as the code already presented in the question). – MrWhite Feb 13 '20 at 19:32