gQ_
gQ_

Reputation: 31

Rewrite URL in XAMPP VirtualHost

Been trying to play with mod_rewrite through .htaccess in one Xampp virtualhost, but I am not getting the results that I am looking for.

What I am tring to do is to rewrite the following: www.example.com/name/billy.html to: www.example.com/billy

Without trying to rewrite the URLs the virtualhost is working fine, I have access to all pages. However, when I add the .htaccess with the corresponding rewrite rule I get a 404 page not found. The regex is working as expected though. I see that the request to www.example.com/name/billy.html it's been rewritten to www.example.com/billy, but the page doesn't load.

The name folder exists in the file structure and the .htaccess is inside the example folder.

Currently, my vh configuration looks like this:

<VirtualHost *:80>
  DocumentRoot "/Applications/XAMPP/xamppfiles/htdocs/example"
  ServerName www.example.com
  <Directory "/Applications/XAMPP/xamppfiles/htdocs/example">
     Options Indexes FollowSymLinks ExecCGI Includes
     AllowOverride All
     Require all granted
  </Directory>
</VirtualHost>

And this is the content of the .htaccess file:

RewriteEngine On
RewriteRule ^name\/([a-z]+).html$ /$1 [L,NC,R]

What is missing?

Upvotes: 3

Views: 836

Answers (1)

MrWhite
MrWhite

Reputation: 45829

What I am tring to do is to rewrite the following: www.example.com/name/billy.html to: www.example.com/billy

You seem to have the process the wrong way round and possibly mixing up "rewrites" and "redirects"?

You should be internally rewriting from the visible "friendly" URL to the underlying file-path that actually handles the request. You are trying to do the opposite here. How is your system expected to handle a request for /billy? (It doesn't, and generates a 404.)

You may be thinking you can change the URL using .htaccess (mod_rewrite) alone? But no, that is not how this works.

RewriteRule ^name\/([a-z]+).html$ /$1 [L,NC,R]

You mention "rewrite", but this directive is in fact a "redirect" (as indicated by the R flag). Specifically, a 302 (temporary) redirect in this instance.

(You might actually want to implement a redirect like this, if you are changing an existing URL structure, but more on that later*1)

A URL "rewrite" is entirely internal to the server. The user only sees the public URL, they do not see the URL that it might be rewritten to. You (the user) can't "see" a rewrite.

A "redirect" on the other hand, usually refers to an external redirect, ie. a 3xx response sent back to the client with an instruction to make a new request to a different URL. The URL being redirected to is visible to the user. This is used when content has moved to a different URL.

So, following your example, you should be requesting/linking to (in your HTML source) the short/friendly URL /billy and internally rewriting the request to /name/billy.html that actually handles the request.

For example:

RewriteEngine On

# Rewrite from "billy" to "name/billy.html"
RewriteRule ^[a-z]+$ name/$0.html [NC,L]

The $0 backreference contains the entire URL-path that is matched by the RewriteRule pattern.

Only use the NC flag if you do need to match uppercase letters as well. But a request for /Billy won't serve /name/billy.html on a case-sensitive OS.

And that's really it, with regards to the URL-rewritting, you can stop reading here.


*1 Redirect from old to new

Regarding the external "redirect" mentioned above. You might choose to implement a redirect (in the opposite direction) if you are changing an existing URL structure and the old URLs have been indexed by search engines and/or linked (or bookmarked) to by external third parties - in order to preserve SEO and keep users happy.

For example, say your original URLs were of the form /name/billy.html and you later decided to change your URLs to /billy instead. You first change the URLs in the HTML source and implement the "rewrite" as mentioned above so the new URLs now work. You then might implement an external redirect from the old /name/billy.html URL to the new /billy URL.

For this, you would use a directive like you had initially, except you have to be careful of redirect-loops because you are already rewriting the request in the opposite directive. You only want to redirect "direct/initial" requests and not rewritten requests by the earlier rewrite (that is actually later in the file). An easy way to check for "direct" requests is to check against the REDIRECT_STATUS environment variable, which is empty on the initial request and set to 200 (as in 200 OK status) when the request is rewritten.

For example, the following "redirect" would go before the above "rewrite", immediately after the RewriteEngine directive:

# Redirect from "name/billy.html" to "/billy"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^name/([a-z]+)\.html$ /$1 [R=301,NC,L]

This should ultimately be a 301 (permanent) redirect since the URL has presumably changed permanently. However, always test with 302 (temporary) redirects to avoid potential caching issues.


Further reading:

Upvotes: 1

Related Questions