mod_alias redirects adding two trailing slashes - .htaccess

I have a number of URLs which may be entered with or without a trailing slash. They're used as vanity URLs.
.htaccess looks like this:
Redirect 301 /folder/vanity1 http://example.com/differentfolder/
Redirect 301 /folder/vanity2 http://example.com/a/third/folder/
Redirect 301 /vanity3 http://example.com/fourth/folder/
Users type in:
http://example.com/folder/vanity1
http://example.com/folder/vanity2
http://example.com/vanity3
http://example.com/folder/vanity1/
http://example.com/folder/vanity2/
http://example.com/vanity3/
When users type these in with no trailing slash, the redirects are working correctly, ending up at http://example.com/differentfolder/ with one trailing slash as desired.
The problem:
When users type these in with a trailing slash, such as http:/example.com/folder/vanity1/ they redirect to a URL with two trailing slashes, such as http://example.com/differentfolder// which throws a 404 error.
I have tried:
Removing all other lines from .htaccess, except these three vanity URLs and the standard WordPress block required to keep the site functioning. The WordPress block appears at the very end of the file:
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
I checked not only with my browser but with WebConfs HTTP Header Check to make sure I wasn't seeing a cached rule. Still the same, if you type a vanity URL with no trailing slash it works; if you type it with one trailing slash, it redirects to two trailing slashes which causes a 404.
Adding a trailing slash in my rules:
Redirect 301 /folder/vanity1/ http://example.com/differentfolder/
This works fine if the user types the trailing slash, but if they leave off the slash it does not redirect and 404s because the URL http://example.com/folder/vanity1/ does not actually exist.
Question:
Is there a different way to 301 redirect the vanity URLs to a final destination with only one trailing slash, no matter which way the visitor types in the vanity URL - with or without a trailing slash?

This works as documented in Redirect,
Additional path information beyond the matched URL-path will be appended to the target URL.
This means /folder/vanity1 works only as a prefix, and an additional slash or anything else in the requested URL will be appended to the target, e.g. http://example.com/folder/vanity1/hi/there would result in http://example.com/differentfolder//hi/there
If you want to match exactly these two URLs, /folder/vanity1 and /folder/vanity1/, you must use either RedirectMatch or mod_rewrite
With a RewriteRule this would look like
RewriteRule ^folder/vanity1/?$ /differentfolder/ [R,L]
This pattern matches both with or without a trailing slash because of /?, which denotes an optional slash. The other rules would look similar.
When everything works as it should, you may replace R with R=301 (permanent redirect). Never test with R=301.
And with RedirectMatch, it would look almost the same
RedirectMatch ^/folder/vanity1/?$ /differentfolder/
Same disclaimer applies here, when it works as expected, you may set the status code to 301.

Related

How to exclude trailing slash on source URL when redirecting?

I have to make a 301 redirect from a domain to another (example-a.com to example-b.com).
This is the code I put in .htaccess of example-a.com:
RewriteEngine on
RewriteRule ^(.*)$ https://www.example-b.com/$1 [R=301,L]
All URLs are the same, except pages of example-a.com all have a / (slash) at the end of the URL, but example-b.com does not. So how to redirect without the ending / if the URL contains one?
RewriteRule ^(.*)$ https://www.example-b.com/$1 [R=301,L]
To exclude the slash from the redirected URL, then exclude the slash from the captured group in the RewriteRule pattern. eg. ^(.*)/$.
However, assuming you also want the document root to redirect, where there is no slash in the URL-path, then you need to make the trailing slash optional (or create an entirely separate rule). But in that case you need to also make the capturing group non-greedy, otherwise the trailing slash will always be included in the captured group (since regex is greedy by default).
So, try the following instead:
RewriteRule ^(.*?)/?$ https://www.example-b.com/$1 [R=302,L]
You will need to clear your browser cache before testing. Since the earlier 301s will have likely been cached.
Note that this is a 302 (temporary) redirect. Only change this to a 301 (permanent) redirect - if that is the intention - once you have confirmed that it works OK. This is to avoid the browser caching erroneous redirects whilst testing.

How can I use htaccess to redirect a URL based on it's segments?

I have some old URLs that have since changed. Right now they are going to my 404 page, so I need to redirect them to the new URLs. The original URLs look like this:
http://example.com/en/blog/events-private/name-of-event
The new URLs look like this, without the "blog" segment:
http://example.com/en/events-private/name-of-event
This is what I'm trying but it's not working:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^/en/blog/events-private/(.*) /en/events-private/$1
</IfModule>
What am I missing?
This isn't using mod_rewrite but this seems to work:
RedirectMatch 301 ^/en/blog/events-private/(.*) /en/events-private/$1
RewriteRule ^/en/blog/events-private/(.*) /en/events-private/$1
In .htaccess the RewriteRule pattern matches against the URL-path after the directory-prefix (that leads to the .htaccess file) has first been removed. The directory-prefix always ends in a slash, so the URL-path that is matched here never starts with a slash. In other words, you simply need to remove the slash prefix:
RewriteRule ^en/blog/events-private/(.*) /en/events-private/$1 [R=301,L]
(and don't forget the R and L flags.)
Note that this contrasts the use of mod_rewrite in a server (or virtualhost) context. In this context you do need the slash prefix! Because in a server context, the pattern matches against the full URL-path.
Also, the RewriteBase directive in this example is superfluous (since you aren't using a relative substitution). And the <IfModule mod_rewrite.c> wrapper should probably be removed (unless this is intended to function on systems without mod_rewrite).

.htaccess folder as parameter to root index.php

What I need is any subfolder to be passed as a parameter to the root index.php
This is the code and it actually works.
RewriteEngine On
RewriteBase /
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} -d
RewriteRule ^(.+?)(/[^/]*|)$ index.php?dir=$1/ [L,QSA]
There is a problem:
When the url is like this (no end slash after 'projects'):
http://example.com/projects
the rewrite rule changes the link in the address bar and it looks like this:
http://example.com/projects/?dir=projects/
Is there a chance the url in the address bar always stays the same(no matter if there is an end slash or not) so the dir parameter is not visible to the user?
I tried with multiple rules - the first one to add an end slash, and then the second rule to pass the directory as parameter, but with no luck so far.
EDIT: so far thanks to w3d I managed to get it working. In the .htaccess just add:
DirectorySlash Off
tl;dr Make the trailing slash mandatory in the RewriteRule pattern (and remove the DirectorySlash Off directive, ie. keep it On).
RewriteRule ^(.+)/$ index.php?dir=$1/ [L,QSA]
As suggested in comments, this "strange" redirect is the result of mod_dir's DirectorySlash On directive, which is On by default. This can be quickly resolved by including DirectorySlash Off at the top of your .htaccess file (or making the trailing slash mandatory - see above and below).
The DirectorySlash On directive instructs Apache to automatically append a slash to URLs that end in a file system directory. In this sense it is "fixing" the URL. mod_dir achieves this with a 301 external redirect.
So, what is actually happening in the above, when DirectorySlash is enabled, is:
Initial request:
/projects (no trailing slash)
Internal rewrite in .htaccess:
/index.php?dir=projects/ (note that the request URL is still /projects)
mod_dir now kicks in and "fixes" the initial request (/projects --> /projects/) by appending a slash to the end of the URL-path. However, the query string from the rewritten URL (above) is passed through:
/projects/?dir=projects/ (this is a 301 external redirect, ie. a new request!)
Internal rewrite in .htaccess (again - new request):
/index.php?dir=projects/&dir=projects/ (note that the request is still /projects/?dir=projects/)
The doubling of the dir=projects/ query param is a result of the QSA flag on the RewriteRule (which I assume is required for other requests?). Your PHP script simply sees a single dir GET param (the later overwrites the former), unless you included dir[]=$1/ in your RewriteRule and you will end up with a 2-element array!
Your RewriteRule pattern also looks unnecessarily complex. You could simply make the trailing slash optional. ie:
RewriteRule ^(.+?)/?$ index.php?dir=$1/ [L,QSA]
Alternatively, having said all the above, you should probably leave DirectorySlash On (default) and simply make the trailing slash mandatory! For example:
RewriteRule ^(.+)/$ index.php?dir=$1/ [L,QSA]
mod_dir will now kick in before your internal rewrite (since it won't match without a trailing slash). This is also better for canonicalising your URLs and there are also potential security risks with turning it off.
Reference:
http://httpd.apache.org/docs/2.2/mod/mod_dir.html#directoryslash

htaccess rewrite rules for directories

I am having trouble with some rewrite rules on an updated site...I am trying to redirect requests for old directories to new pages, like this:
http://www.mysite.com/olddirectory --> http://www.mysite.com/this-is-the-new-page
the following rule works, but not with a trailing slash on the directory:
ie: /olddirectory redirects correctly, but /olddirectory/ doesn't
RewriteRule ^olddirectory$ this-is-the-new-page [R=301,NC,L]
Any ideas on how to get it to recognise the trailing slash on the dir?
The following rule will do redirect with or without trailing slash:
RewriteRule ^olddirectory/?$ /this-is-the-new-page [NC,R=301,L]
The key is to make trailing slash optional, and that's what ? does: /?

301 redirect - add a slash for index page

How do you set a 301 redirect in .htaccess to add the forward slash to your document root if someone links to you without it?
According to the research I have done most search engines consider the following URL's as two different URL's.
mydomain.com (no forward slash)
mydomain.com/ (forward slash)
I've tried this (plus many others):
RewriteRule ^$ http://www.mydomain.com/ [R=301,L]
That throws it into a loop loading the page over and over.
I think you drew incorrect conclusions out of your research. For HTTP, the Root-URL without forward slash is specified to be equal to the forward slash:
Note that the absolute path cannot be empty; if none is present in the original URI, it MUST be given as "/" (the server root). [RFC 2616 Section 5.1.2]
Hence, if the original URL is only a domain name that does not end with a forward slash (i.e. the absolute path would be empty), that URL will be extended by a forward slash. You don't have to do anything.
As for your problem: Due to the subtleies of mod_rewrite, the first slash is ommited, so your RewriteRule captured the root URL and sent the requester in a redirection-loop.
You can try something like that
ensure you are matching a domain without a / and if not do the redirection.
RewriteCond %{HTTP_HOST} ^mydomain\.com$
RewriteRule ^(.*)$ http://mydomain.com/$1 [L,R=301]

Resources