htaccess replace all slash (two and more) by one slash - regex

I want to modify an .htaccess file to do 2 things:
Force the addition of a slash at the end of each URL
Replace the excess slashes (2 and more) by 1 single slash.
Here is a set of URLs and their expected result:
https://www.mywebsite.io/fr > https://www.mywebsite.io/fr/
https://www.mywebsite.io/fr/register > https://www.mywebsite.io/fr/register/
https://www.mywebsite.io/fr/register// > https://www.mywebsite.io/fr/register/
https://www.mywebsite.io/fr////register > https://www.mywebsite.io/fr/register/
https://www.mywebsite.io/fr/register///other////// > https://www.mywebsite.io/fr/register/other/
I manage to do the first step which is to force a final / but I cannot replace the extra /.
Can you help me with this problem?
Here is the content of my .htaccess file
Options +FollowSymlinks
RewriteEngine On
## Force HTTPS
RewriteCond %{HTTPS} !on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI}/$1 [L,R]
## Force slash
RewriteCond %{REQUEST_URI} /+[^\.]+$
RewriteRule ^(.+[^/])$ %{REQUEST_URI}/ [L,R]
## Remove repeated slashs
#RewriteCond %{REQUEST_URI} ^(.*)/{2,}(.*)$ [N]
#RewriteRule (.*) %1 [R,L]
RewriteRule (.*)/{2,}(.*) $1/$2 [N]
RewriteRule ^(fr|en){1}(/?)$ content/index.php?lang=$1 [L]
RewriteRule ^(fr|en){1}(/){1}(registration){1}(/?)$ content/registration.php?lang=$1 [L]

Have it like this: (see comments inline)
Options +FollowSymlinks
RewriteEngine On
## add https and trailing slash in same rule
RewriteCond %{HTTPS} !on [OR]
RewriteCond %{REQUEST_URI} !/$
RewriteRule ^(.*?)/?$ https://%{HTTP_HOST}/$1/ [R=301,L,NE]
## Remove repeated slashs
RewriteCond %{THE_REQUEST} \s[^?]*//
RewriteRule ^.*$ /$0 [R=301,L,NE]
RewriteRule ^(fr|en)/?$ content/index.php?lang=$1 [L,QSA]
RewriteRule ^(fr|en)/registration/?$ content/registration.php?lang=$1 [L,QSA]

With your shown samples, could you please try following. I have also fixed your small issues of missing flags too in your question. Please make sure you clear your browser cache before testing your URLs.
Options +FollowSymlinks
RewriteEngine On
## Force HTTPS
RewriteCond %{HTTPS} !on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}/$1 [L,R]
## make slashes to 1 at last or uri, in between slashes will be automatically moved to single slash.
RewriteRule ^([^/]*)(?:/+)$ %{REQUEST_URI}/ [L,R=301]
RewriteRule ^(fr|en)/?$ content/index.php?lang=$1 [NC,L]
RewriteRule ^(fr|en)/(registration)(/?)$ content/$2.php?lang=$1 [NC,L]

Related

Redirect to default language except for /amp/ URLs

I'm struggling to solve a redirection but without any success.
I changed the URLs of my site forcing a default language, before it was site.com/help/ and now it's site.com/en/help/. Thanks to help from Stack Overflow I made the redirection, but then I faced a new problem with the AMP pages: site.com/amp/help/ are now redirected to site.com/en/amp/help/ while they are supposed to be site.com/amp/en/help/.
Again, thanks to help on this site, I changed the structure of URLs to site.com/en/help/amp/ (amp always at the end). To achieve this, I had to delete the .php extension I had in some pages and also decided to remove the trailing slash.
I'm now facing two new issues: the 301 redirection to a non .php page and URLs with trailing slashes to a non trailing slash don't work. Below is my htaccess code.
RewriteEngine on
# amp
RewriteRule ^(.*/)?amp/(.+?)/?$ /$1$2/amp [R=301,NC,L]
## redirect to default language (fr)
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond %{REQUEST_URI} !/inc
RewriteCond %{REQUEST_URI} !/ajax/
RewriteCond %{REQUEST_URI} !/img/
RewriteRule ^(?![a-z]{2}(?:[/-]|$))(.*)$ /fr/$1 [R=301,L,NE]
## Unless directory, remove trailing slash
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)/$ /$1 [R=301,NE,L]
## add trailing slash in front of directories
RewriteCond %{DOCUMENT_ROOT}/$1 -d
RewriteRule ^[a-z]{2}(?:-[a-z]{2})?/(.+)$ /$1/ [L]
# remove .php
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond %{REQUEST_FILENAME} !global.js
RewriteCond %{REQUEST_URI} !/ajax/
RewriteCond %{REQUEST_URI} !results.php
RewriteRule ^(.+)\.php(.*)$ /$1$2 [R=301,NC,NE,L]
## amp pages
RewriteRule ^(.*)/amp$ /$1?amp=1 [NC,QSA,L]
## folders of languages
#RewriteRule ^([a-z]{2}(?:-[a-z]{2})?)/(.*)$ /$2?lang=$1 [QSA,L]
RewriteRule ^([a-z]{2}|[a-z]{2}-[a-z]{2})$ /$2?lang=$1 [QSA,L]
RewriteRule ^([a-z]{2}|[a-z]{2}-[a-z]{2})/(.*)$ /$2?lang=$1 [QSA,L]
## hide .php extention
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteCond %{REQUEST_FILENAME} !global.js
RewriteRule ^(.+?)/?$ $1.php [L]
Have it this way:
RewriteEngine on
# changed amp URLs
RewriteRule ^(.*/)?amp/(.+?)/?$ /$1$2/amp/ [R=301,NC,L]
## redirect to default language (en)
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond %{REQUEST_URI} !/img/
RewriteRule ^(?![a-z]{2}(?:[/-]|$))(.*)$ /en/$1 [R=301,L,NE]
## Unless directory, remove trailing slash
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !/amp/$ [NC]
RewriteRule ^(.+)/$ /$1 [R=301,NE,L]
## add trailing slash in front of directories after lang rewrite
RewriteCond %{DOCUMENT_ROOT}/$1 -d
RewriteRule ^[a-z]{2}(?:-[a-z]{2})?/(.+[^/])$ /$0/ [L]
# remove .php
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteRule ^(.+)\.php$ /$1/ [R=301,NC,NE,L]
## amp pages
RewriteRule ^(.+/)amp/?$ /$1?amp=1 [NC,QSA,L]
## folders of languages
RewriteRule ^([a-z]{2}(?:-[a-z]{2})?)/(.*)$ /$2?lang=$1 [QSA,L]
## hide .php extention
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteCond %{REQUEST_FILENAME} !global.js
RewriteRule ^(.+?)/?$ $1.php [L]
Explanation of this trailing slash rule:
## add trailing slash in front of directories after lang rewrite
RewriteCond %{DOCUMENT_ROOT}/$1 -d
RewriteRule ^[a-z]{2}(?:-[a-z]{2})?/(.+[^/])$ /$0/ [L]
Take an example URI: /fr/cart.
In a later rule we remove lang component from URL and pass it as lang=<fr|en> query parameter. Part after lang parameter e.g. /cart doesn't have a trailing slash and if it is a real directory then /cart?lang=fr will be redirected to /cart/?lang=fr by Apache's mod_dir module and your internal URL will be exposed in browser.
So in this current rule we capture part after lang component and check if we don't have a trailing slash and it is a directory then this rule internally rewrites to /fr/cart/ with a trailing slash. Later rule then rewrites it to /cart/?lang=fr and mod_dir doesn't redirect anymore.
#anubhava's solution works perfectly well except for one little case: /fr/amp/page.php redirects to /fr/page.php/amp, I had to make some changed to the code and managed to make it work. Below is the updated code with small changes I made:
1- removed some slashes at the end of some rules as I don't need them any more
2- removed this rule RewriteCond %{REQUEST_URI} !/amp/$ [NC]
3- to fix the .php problem, I replaced RewriteRule ^(.+)\.php$ /$1/ [R=301,NC,NE,L] by RewriteRule ^(.+)\.php(.*)$ /$1$2 [R=301,NC,NE,L].
RewriteEngine on
## changed amp URLs
RewriteRule ^(.*/)?amp/(.+?)/?$ /$1$2/amp [R=301,NC,L]
## redirect to default language (en)
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond %{REQUEST_URI} !/img/
RewriteRule ^(?![a-z]{2}(?:[/-]|$))(.*)$ /en/$1 [R=301,L,NE]
## Unless directory, remove trailing slash
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)/$ /$1 [R=301,NE,L]
## add trailing slash in front of directories
RewriteCond %{DOCUMENT_ROOT}/$1 -d
RewriteRule ^[a-z]{2}(?:-[a-z]{2})?/(.+)$ /$1/ [L]
# remove .php
RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteRule ^(.+)\.php(.*)$ /$1$2 [R=301,NC,NE,L]
## amp pages
RewriteRule ^(.+/)amp$ /$1?amp=1 [NC,QSA,L]
## folders of languages
RewriteRule ^([a-z]{2}(?:-[a-z]{2})?)/(.*)$ /$2?lang=$1 [QSA,L]
## hide .php extention
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteCond %{REQUEST_FILENAME} !global.js
RewriteRule ^(.+?)/?$ $1.php [L]

mod-rewrite when index.php is in the URL

I wish to rewrite http://example.com/admin/pages/index.php to http://example.com/index.php?admin=admin&cid=pages.
It works for http://example.com/admin/pages (which I also want), but not for http://example.com/admin/pages/index.php.
How is this accomplished?
RewriteEngine On
RewriteBase /
# If the request is for a valid directory, file, or link, don't do anything
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -l
RewriteRule ^ - [L]
#remove the trailing slash
RewriteRule (.+)/$ $1
# If you add this first rule to support views, be sure to remove the QSA flag from the second rule (maybe not required since the first rule has the L flag)
#replace admin/cid with index.php?admin=admin&cid=cid
RewriteRule ^([^/]+)/([^/]+)/?$ index.php?admin=$1&cid=$2 [L,QSA]
#replace mypage with index.php?admin=mypage
RewriteRule ^([^/]+)/?$ index.php?admin=$1 [L,QSA]
EDIT
Random changes shows that this might be correct.
RewriteRule ^([^/]+)/([^/]+)/index.php/?$ index.php?admin=$1&cid=$2 [L,QSA]
RewriteRule ^([^/]+)/([^/]+)/?$ index.php?admin=$1&cid=$2 [L,QSA]
#replace mypage with index.php?admin=mypage
RewriteRule ^([^/]+)/index.php/?$ index.php?admin=$1 [L,QSA]
RewriteRule ^([^/]+)/?$ index.php?admin=$1 [L,QSA]
Problem is your first rule that is skipping all files & directories from rules.
Replace your rules with this:
RewriteEngine On
RewriteRule ^index\.php$ - [L,NC]
#remove the trailing slash
RewriteRule (.+)/$ $1 [L,R=302]
# If you add this first rule to support views, be sure to remove the QSA flag from the second rule (maybe not required since the first rule has the L flag)
#replace admin/cid with index.php?admin=admin&cid=cid
RewriteRule ^([^/]+)/([^/]+)(?:/?|/index\.php)$ index.php?admin=$1&cid=$2 [L,QSA]
#replace mypage with index.php?admin=mypage
RewriteRule ^([^/.]+)/?$ index.php?admin=$1 [L,QSA]

Flat links with htaccess

What code can I use to have flat links with htaccess?
An example of what I want:
site.com/folderA becomes : site.com/index.php?section=folderA
site.com/folderA/folderB becomes : site.com/index.php?section=folderA&action=folderB
site.com/folderA/folderB/folderC becomes : site.com/index.php?section=folderA&action=folderB&id=folderC
I tried it with the following code:
Options +FollowSymLinks
RewriteEngine On
RewriteRule ^([^/]+) index.php?section=$1 [NC]
RewriteRule ^([^/]+)/([^/]+) index.php?section=$1&action=$2 [NC]
RewriteRule ^([^/]+)/([^/]+)/([^/]+) index.php?section=$1&action=$2&id=$3 [NC]
Make sure to use $ (line end anchor) to avoid matching unwanted URL parts:
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
# if request is not for a file/directory
RewriteCond %{SCRIPT_FILENAME} -d [OR]
RewriteCond %{SCRIPT_FILENAME} -f
# then skip from rewrites
RewriteRule ^ - [L]
RewriteRule ^([^/]+)/?$ index.php?section=$1 [NC,L,QSA]
RewriteRule ^([^/]+)/([^/]+)/?$ index.php?section=$1action=$2 [NC,L,QSA]
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/?$ index.php?section=$1action=$2id=$3 [NC,L,QSA]

mod_rewrite: remove www and trailing slash

I already have a rule set up to remove www from my urls and redirect them...
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*) http://'%'1$1 [R=301,L]
*note-I had to put quotes around the percent sign to post this message, the actual rule does not contain them.
I now want to also strip off any trailing /
How would I do this?
Add an extra rule:
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
# remove trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+?)/$ $1 [L,R=301]

ExpressionEngine mod_rewrite Rule to Redirect URLs with Underscores to Dashes

I'm using ExpressionEngine as my CMS and would like to remove underscores from my site's URLs and replace them with dashes.
For example, I've got a URL that is formatted like this:
http://example.com/index.php/menu/friday-lunch
To remove index.php from the URL, I'm using the following mod_rewrite rule:
RewriteCond $1 !\.(gif|jpe?g|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
Which works, since I can just type in: http://example.com/menu/friday-lunch
On the old site I used underscores instead of hyphens for page URIs, so I wrote a mod_rewrite rule to to redirect URIs with underscores to use dashes.
So friday_lunch becomes friday-lunch using the following RewriteRule:
RewriteRule ^([^_]*)_([^_]*_.*) $1-$2 [N]
RewriteRule ^([^_]*)_([^_]*)$ /$1-$2 [L,R=301]
This rule works rather well, except that it 301 Redirects to example.com/index.php/menu/friday-lunch instead of example.com/menu/friday-lunch — notice the addition of index.php.
Here's the entire .htaccess I'm currently using:
<IfModule mod_rewrite.c>
RewriteEngine On
# Removes index.php
RewriteCond $1 !\.(gif|jpe?g|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
RewriteRule ^([^_]*)_([^_]*_.*) $1-$2 [N]
RewriteRule ^([^_]*)_([^_]*)$ /$1-$2 [L,R=301]
</IfModule>
How can I redirect all of my URLs with underscores to the equivalent with dashes?
Bonus: to make matters worse, URLs that lead to /system, must not be rewritten with a hyphen, e.g.: example.com/system/login_in/.
Here's a complete set of RewriteRules that should do what you need:
<IfModule mod_rewrite.c>
# Enable Apache's RewriteEngine
RewriteEngine On
# Ignore Matching Directories
RewriteRule ^(images|themes|system) - [L,NC]
# Replace Underscores with Dashes
RewriteRule ^([^_]*)_([^_]*)_(.*)$ /$1-$2-$3 [R=301,L]
RewriteRule ^([^_]*)_(.*)$ /$1-$2 [R=301,L]
# Remove index.php from ExpressionEngine URLs
RewriteCond $1 !\.(gif|jpe?g|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php/$1 [L]
</IfModule>
To have your mod_rewrite rules ignore the ExpressionEngine system folder and not replace underscores _ with dashes - use the following:
RewriteRule ^(images|themes|system) - [L,NC]
Dissecting the RewriteRule into plain English:
The - flag instructions Apache to do nothing, and to not rewrite the URI
The L flags means this should be last rule; ignore everything following
The NC flag means no-case (so "System" or "SYSTEM" is also matched)
This "ignore" rule is especially important and you may need to add additional directories to exclude depending on your directory structure.
Otherwise, you may end up with images and other files saved with underscores that get replaced with dashes.
Note: If your URLs contain more than three underscores, you'll need to add another RewriteRule above the existing ones for each Word Separator for URL Titles you want to replace:
RewriteRule ^([^_]*)_([^_]*)_(.*)_(.*)_(.*)$ /$1-$2-$3-$4-$5 [R=301,L]
RewriteRule ^([^_]*)_([^_]*)_(.*)_(.*)$ /$1-$2-$3-$4 [R=301,L]
You included 'index.php' in your replacement string.
RewriteRule ^(.*)$ index.php/$1 -> RewriteRule ^(.*)$ $1