Home > Software engineering >  Redirect php id Properly with .htaccess OpenLiteSpeed Server
Redirect php id Properly with .htaccess OpenLiteSpeed Server

Time:06-01

i am new to .htaccess usage and tried to learn through online resources but however i write it the rules negate each other and am having a hard time writing a good enough .htaccess file below is my current .htaccess file which works fine for some pages like removing extensions and rewriting subdomains please check below

## Flag for GoDaddy
Options  MultiViews

RewriteBase /

## Remove extensions
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !=f
RewriteRule ^([^\.] )$ $1.php [NC,L]

## Redirect from extensions to non-extensions
RewriteCond %{THE_REQUEST} \s/ (. ?)\.php[\s?] [NC]
RewriteRule ^ /%1 [R=301,NE,L]

## Redirect Pages
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^post/([a-zA-Z0-9-/] )$ /post.php?ps=$1
RewriteRule ^([a-zA-Z0-9-/] )$ post-files.php?ps=$1 [L,QSA]

## Server Only

## Redirect from www - non-www
RewriteCond %{HTTP_HOST} ^www\.(. )$ [NC]
RewriteRule ^(.*)$ http://$1/$1 [R=301,L]


## SSL Redirect
## RewriteEngine On
## RewriteCond %{HTTPS} ≠On
## RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

## Create Error Pages
ErrorDocument 404 /errors/404.html
ErrorDocument 403 /errors/403.html
ErrorDocument 500 /errors/500.html

## Redirect non-existing pages to index.php
Options  SymLinksIfOwnerMatch
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

Above is the .htaccess am currently using and i got it through tutorials from youtube it works good and redirects files.php to files only which am happy with but as you can see above this line RewriteRule ^post/([a-zA-Z0-9-/] )$ /post.php?ps=$1 and the line below it are not passing through the ps but they show 404 page

i want the results to be domain.com/post-file-slug to go to exactly file domain.com/post-file.php?ps=post-slug-here for rule RewriteRule ^([a-zA-Z0-9-/] )$ post-files.php?ps=$1 [L,QSA]

and

domain.com/post/post-slug-here to go to exactly domain.com/post.php?ps=post-slug-here for rule RewriteRule ^post/([a-zA-Z0-9-/] )$ /post.php?ps=$1

I was working on this for 2 days now hopefully fix it soon. Thanks

CodePudding user response:

## Flag for GoDaddy
Options  MultiViews

In what way is this a "flag for GoDaddy"? Enabling MultiViews will cause the ps URL parameter not to be passed to the post.php script. You need to ensure that MultiViews is disabled for the later rewrites to work as intended. ie.

Options -MultiViews

MultiViews (part of mod_negotiation) essentially enables extensionless URLs. It will result in a request for /post/post-slug-here to be "rewritten" to /post.php/post-slug-here before your mod_rewrite directive is processed, so it never matches and never rewrites the request to include the ps URL parameter.

## Remove extensions
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !=f
RewriteRule ^([^\.] )$ $1.php [NC,L]

## Redirect from extensions to non-extensions
RewriteCond %{THE_REQUEST} \s/ (. ?)\.php[\s?] [NC]
RewriteRule ^ /%1 [R=301,NE,L]

It is currently MultiViews that is allowing your extensionless URLs to work. The first condition (RewriteCond directive) above is incorrect. It should be !-f (not a file), not !=f (does not equal "f" - always true). However, this is still "wrong", as you need to check that the .php file exists before rewriting the request. If you simply rewrite all requests that do not map to a file (which is what you are trying to do here) then the later rewrites to post.php, post-files.php and index.php will not be processed as intended.

The regex \s/ (. ?)\.php[\s?] in the second condition is not strictly correct as it will result in a malformed redirect if .php occurs in the query string when it is omitted in the URL-path. eg. A request for /foo?bar.php would result in a redirect to /foo?bar when there should be no redirect at all in this instance. The regex needs to capture the URL-path only, so change the subpattern (. ?) to ([^?] ) instead.

These two rules are also the wrong way round. The external redirect should be first. As a general rule, external redirects should always go before internal rewrites.

It should be like this instead:

## Remove extensions
 
## Redirect to remove ".php" extension
RewriteCond %{THE_REQUEST} \s/ ([^?] ?)\.php[\s?] [NC]
RewriteRule \.php$ /%1 [R=301,NE,L]

# Rewrite to append ".php" extension if corresponding ".php" file exists
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^([^.] )$ $1.php [L]

You should already be linking to the file without the .php extension. The redirect to remove the .php extension is for SEO only when changing an existing URL structure.

No need to backslash-escape a literal dot when used inside a regex character class. The NC flag was superfluous here.

## Redirect Pages
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^post/([a-zA-Z0-9-/] )$ /post.php?ps=$1
RewriteRule ^([a-zA-Z0-9-/] )$ post-files.php?ps=$1 [L,QSA]

RewriteCond directives only apply to the first RewriteRule directive that follows. So, the second rule above is processed unconditionally - is that the intention?

In fact, those two condition are probably superfluous. The regex would already appear to exclude actual files since the regex excludes dots. And do you need to be able to access filesystem directories directly?

The character class [a-zA-Z0-9-/] is "confusing". The last hyphen is seen as a literal hyphen (which is presumably the intention), but at first glance it can look like a range specifier (as used earlier in the character class). To avoid confusion when matching a literal hyphen inside a character class, either backslash-escape it, or move it to the first or last character in the character class. eg. [a-zA-Z0-9/-].

You are also missing the L flag from the first rule. (You've included it on the second.) Do you also need the QSA flag? (ie. Are you expecting additional URL parameters on the initial request?)

Having revised the "extension removal" rules above, this does not matter so much, but these rules that rewrite the request to post.php and post-files.php should really be above the "extension removal" rules.

## Redirect from www - non-www
RewriteCond %{HTTP_HOST} ^www\.(. )$ [NC]
RewriteRule ^(.*)$ http://$1/$1 [R=301,L]

This rule is incorrect and in the wrong place. Canonical redirects (www to non-www and HTTP to HTTPS) should generally be above other rules. As mentioned above, redirects before rewrites.

But this rule is also wholly incorrect. $1 is a backreference to the first captured subpattern in the RewriteRule, so http://$1/$1 will naturally result in a malformed redirect. The first backreference should be %1 (to the last matched CondPattern) to match the requested hostname. Ordinarily, you should also be redirecting to HTTPS here, not HTTP. For example, the rule should read:

:
RewriteRule (.*) https://%1/$1 [R=301,L]

The ^ and $ surrounding the RewriteRule pattern are superfluous since regex is greedy by default.

## SSL Redirect
## RewriteEngine On
## RewriteCond %{HTTPS} ≠On
## RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

Although commented out, it is also incorrect. It needs to go before the other rewrites. It should go at the top of the file if implementing HSTS or after the www to non-www redirect if not (and minimising the number of redirects).

The CondPattern in the preceding condition should be !on, not ≠On (which is wholly invalid on two counts... is not valid and the comparison is case-sensitive. HTTPS will always be lowercase.)

You are also missing the R=301 and L flags.

No need for a capturing group in the RewriteRule pattern, since this is not being used in the substitution string. ^ would suffice (and be more efficient) instead of (.*).

## Create Error Pages
ErrorDocument 404 /errors/404.html
ErrorDocument 403 /errors/403.html
ErrorDocument 500 /errors/500.html

For readability, you should define your custom error documents at the top of the file. (Technically, it doesn't matter.)

## Redirect non-existing pages to index.php
Options  SymLinksIfOwnerMatch
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

For readability you should define the Options together at the top of the file (with -MultiViews). For example:

Options -MultiViews -Indexex  SymLinksIfOwnerMatch

(Disabling Indexes - auto-generated directory listings - is a good idea.)

You do not need to repeat the RewriteEngine directive. (Only the last instance of this directive does anything.) It is logical to place this rule near the top of the file, before your first mod_rewrite directive. (Although technically, the position of this directive in the file does not actually matter.)

Aside: You should be consistent in the prefix you use on your internal rewrites. On some rules you include the slash prefix (eg. /post.php), and on some you have omitted it (post-files.php). You have defined RewriteBase / (which isn't strictly required here as it happens) - RewriteBase only applies to relative substitution strings (ie. when the slash prefix is omitted).


UPDATE:

also i have file i want to exclude like 404.php in root directory from how do i exclude somefiles from the redirect. when i sent ajax to backend php file it redirected to homepage and failed to retrieve data.

To exclude specific files you would add a rule like the following, after the canonical redirects:

# Exclude "/404.php" from stripping the ".php" extension
RewriteRule ^404\.php$ - [L]

Generally, once you go extensionless for .php files you should be extensionless everywhere. So, there should be no unexpected redirects. The redirect is really only for SEO.

With regards to your AJAX requests, if you are making POST requests, then you could simply exclude all POST requests from further processing. For example:

# Prevent further processing of POST requests to ".php" files
RewriteCond %{REQUEST_METHOD} POST [NC]
RewriteRule \.php$ - [L]

Alternatively (or as well as), if your AJAX requests are setting a custom HTTP request header then you can check for this as well.


Summary

Bringing the above points together, it should look like this:

## Disable MultiViews and Indexes
Options -MultiViews -Indexes  SymLinksIfOwnerMatch

## Create Error Pages
ErrorDocument 404 /errors/404.html
ErrorDocument 403 /errors/403.html
ErrorDocument 500 /errors/500.html

RewriteEngine On

RewriteBase /

#### Canonical redirects

## SSL Redirect
## RewriteCond %{HTTPS} !on
## RewriteRule (.*) https://%{HTTP_HOST}/$1 [R=301,L]

## Redirect from www - non-www
## >>> CHANGE TO "HTTPS://"
RewriteCond %{HTTP_HOST} ^www\.(. ) [NC]
RewriteRule (.*) http://%1/$1 [R=301,L]

#### Rewrite Pages

RewriteRule ^post/([a-zA-Z0-9/-] )$ post.php?ps=$1 [QSA,L]
RewriteRule ^([a-zA-Z0-9/-] )$ post-files.php?ps=$1 [QSA,L]

#### Exceptions

## Exclude "/404.php" from stripping the ".php" extension
RewriteRule ^404\.php$ - [L]

## Prevent further processing of POST requests to ".php" files
RewriteCond %{REQUEST_METHOD} POST [NC]
RewriteRule \.php$ - [L]

#### Remove extensions

## Redirect to remove ".php" extension
RewriteCond %{THE_REQUEST} \s/ ([^?] )\.php[\s?] [NC]
RewriteRule \.php$ /%1 [R=301,NE,L]

## Rewrite to append ".php" extension if corresponding ".php" file exists
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^([^.] )$ $1.php [L]

## Redirect non-existing pages to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]
  • Related