Skip to content
Advertisement

Customize URL rewriting with htaccess

I have the following in my htaccess:

RewriteEngine On
RewriteRule ^reset-password/?$ /pwreset.php [NC,L]

It works perfectly, the question is whether it would also be possible to automatically redirect it to the new path when the PHP file is accessed directly?

For example: example.com/pwreset.php >> example.com/reset-password

There are several pages, so I needed something practical.

UPDATE:

RewriteRule .* - [E=URL:https://user.example.com]
RewriteRule ^pwreset.php$ %{ENV:URL}/reset-password [R=301,L]
RewriteRule ^reset-password$ pwreset.php [END]
RewriteRule ^clientarea.php$ %{ENV:URL}/home [R=301,L]
RewriteRule ^home$ clientarea.php [END]
RewriteRule ^logout.php$ %{ENV:URL}/logout [R=301,L]
RewriteRule ^logout$ logout.php [END]

The difficulty is with links like this: user.example.com/clientarea.php?action=services

When accessed it redirects to: user.example.com/home?action=services

I need that in the case of these links the redirection occurs to: user.example.com/services

Can you help me with this new rule? And also how to simplify it all.

Advertisement

Answer

To externally redirect direct requests for /pwreset.php to /reset-password you can do something like the following before your existing internal rewrite:

RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^pwreset.php$ /reset-password [R=301,L]

The condition that checks against the REDIRECT_STATUS env var is necessary in order to avoid a redirect loop, since we only want to redirect direct requests the user has made and not rewritten requests by the other directive. REDIRECT_STATUS is empty on the initial request and set to “200” (as in 200 OK) after the first successful rewrite.

Test first with a 302 (temporary) redirect to avoid potential caching issues.


UPDATE#1: is there any way to put that in the same rule? Because there are many pages, then I would have to practically duplicate each line.

No, if you are using a single .htaccess file and you want to do everything via .htaccess then you would need to repeat the two directives (RewriteCond and RewriteRule) for each external redirect. Without the RewriteCond directive (condition), the rewritten request would also be redirected – creating an endless loop.

However, you could use a 2nd .htaccess file in a subdirectory to handle the redirects. This would avoid the additional condition for every rule.

For example, instead of redirecting pwreset.php as explained above. You could internally rewrite all requests for .php files (assuming you are not accessing any .php files directly) to a subdirectory that contains another .htaccess with the necessary redirects.

  1. Change the above redirect to a rewrite of the form:

    # Rewrite all direct requests for `.php` files to a subdirectory
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule (.+.php)$ /redirect-handler/$1 [L]
    

    Where /redirect-handler is a physical subdirectory.

  2. Create /redirect-handler/.htaccess with the necessary redirects:

    # Redirect direct requests for .php files to the canonical URL.
    # If we are here then we know that the .php file has been called directly
    # so do not need an additional condition before each rule.
    
    RewriteEngine On
    
    RewriteRule ^pwreset.php$ /reset-password [R=301,L]
    RewriteRule ^another.php$ /canonical-url [R=301,L]
    

Another alternative is to rewrite the request to a PHP script, instead of a subdirectory containing another .htaccess file and perform the redirect in PHP instead.

For example, your initial “redirect” becomes:

# Rewrite all direct requests for `.php` files to PHP script
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} !^/redirect-handler.php$
RewriteRule (.+.php)$ /redirect-handler.php?url=$1 [L]

Then, in /redirect-handler.php you would check the $_GET['url'] var for the PHP file being requested. (Or just check the $_SERVER['REQUEST_URI'] superglobal.) Lookup the desired canonical URL and either 301 “redirect” if the request maps to a valid URL or return a 404.

You could take this a step further and manage everything in PHP – in a single script. This has the obvious advantage that you have just a single list (PHP assoc array or DB table) that contains the PHP file/canonical-url mapping. Apart from the initial rewrites in .htaccess, you would not need to update .htaccess when URLs are added/removed/updated. Although you might need modify your existing pages, depending on how you are including files etc. (?)


UPDATE#2: Using a single .htaccess file… If you are on Apache 2.4 (as opposed to 2.2) then you can just use the END flag on the internal rewrite (instead of the L flag) and this will avoid having to specify the additional condition (RewriteCond directive) on the “redirect”.

For example:

RewriteEngine On

# Canonical redirects...
RewriteRule ^pwreset.php$ /reset-password [R=301,L]
:

# Internal rewrites...
RewriteRule ^reset-password$ pwreset.php [END]
:

Unlike the L flag, the END flag stops all processing, thus preventing the rewrite engine starting over.

Note that I made a few small changes to your initial rewrite…

  • Removed the NC flag – do you really need a case-insensitive match? This would otherwise potentially create a duplicate content issue, but you should be consistently linking to a single canonical URL.
  • Removed the optional trailing slash on the RewriteRule pattern. (Same reason as above.)
  • Removed the slash prefix on the RewriteRule substitution string – it’s not required and simplifies the rewrite (it now rewrites to a filesystem path – which is what it is – rather than a URL-path).

This is much simpler than the methods outlined above! (Although those methods do still have their purpose.)

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement