URL mod-rewriting - .htaccess

I want to mod_rewrite this Url:
Before:
website.altervista.org/page.php?name=value
After:
website.altervista.org/value

Solution:
RewriteCond %{REQUEST_URI} !page.php$
RewriteRule ^(.+)$ /page.php?name=$1 [L]
Explanation:
The mod_rewrite RewriteRule has 3 parameters:
Pattern
Substitution
Flags
Implemented as such:
RewriteRule pattern substitution [flags]
Starting at server root, enter the requested URL path in the RewriteRule "pattern" parameter, and the desired path in the "substitution" parameter. In this case:
RewriteRule ^(.+)$ /page.php?name=$1 [L]
If the URL varies and you don't want to (or can't) write a rule for every situation then use the regular expression ^(.+)$ to capture the dynamic value and inject it into your substituted path using the RE capture variable $1. The first set of parenthesis is $1, the second set is $2, etc. And capturing parenthesis can be nested.
^(.+)$ This regular expression can be read as: ^ at the start of the string, $ all the way to the end of the string, look for . any character + one or times and () capture that value into a variable.
Problem:
Even though we have the flag [L] (last rule evaluated), the mod_rewrite engine (behind the scenes) sends the newly constructed request /page.php?name=somevalue back through the mod_rewrite engine until no rules are met or, apparently, there are no changes to the request. Fortunately there is a supplimentary directive to expand on the conditional power provided by the RewriteRule called RewriteCond.
The mod_rewrite RewriteCond applies to the next occurring RewriteRule and also has 3 parameters:
Test String
Conditional Pattern
Flags (optional)
The Test String can be derived from a few sources. Often a Server Variable, relating to the current request, is used here as the subject of this condition.
The Conditional Pattern is, again, text or a regular expression, but has some additional special conditions that may be evaluated. Read the Apache online mod_rewrite documentation for a detailed explanation.
In this case: RewriteRule ^(.+)$ /page.php?name=$1 [L], our newly substituted request is sent back through mod_rewrite as /page.php?name=somevalue and matches our "catch-all" rule, therefore our original "somevalue" is lost and replaced with our newly requested resource page.php. To prevent our "catch all" from catching our "page.php" requests let's exclude it from the rule using RewriteCond.
RewriteCond %{REQUEST_URI} !page.php$
RewriteRule ^(.+)$ /page.php?name=$1 [L]
This RewriteCond can be read as: %{REQUEST_URI} get the requested resource and does it ! NOT $ end with page.php. If this condition is true, continue to the next condition or rule. If this condition is not true, skip this rule set and continue to the next rule set.

Related

Rewrite URL with query parameters in .htaccess

Say I have these urls:
https://example.com/bbs/board.php?bo_table=cad
https://example.com/bbs/board.php?bo_table=videos
https://example.com/bbs/board.php?bo_table=news
How can I rewrite these in .htaccess to something like this:
https://example.com/cad
https://example.com/videos
https://example.com/news
This is my attempt thus far. I know that my rewrite method is solid because it works on URL's without query strings. I tried the QSA flag (Query String Append) to no avail.
Options -MultiViews
RewriteRule ^bbs/board.php?bo_table=cad$ /caster-cad-downloads [R=301,L,QSA]
RewriteRule ^caster-cad-downloads$ bbs/board.php?bo_table=cad [END]
RewriteRule ^bbs/board.php?bo_table=video$ /caster-videos [R=301,L,QSA]
RewriteRule ^caster-videos$ bbs/board.php?bo_table=video [END]
RewriteRule ^bbs/board.php?bo_table=news$ /news [R=301,L,QSA]
RewriteRule ^news$ bbs/board.php?bo_table=news [END]
How can I rewrite to a different URL instead of the query string while still using the
%{QUERY_STRING method?
RewriteCond %{QUERY_STRING} ^bo_table=(cad|videos|news)$
RewriteRule ^bbs/board\.php$ /%1 [QSD,R=301,L]
# RewriteRule ^(caster-cad-downloads|caster-videos|news)$ bbs/board.php?bo_table=$1 [END]
RewriteRule ^(?:caster-(cad)-downloads|caster-(videos)|(news))$ bbs/board.php?bo_table=$1 [END]
How can I rewrite these in .htaccess to something like this:
The "rewrite" is the other way round (as mentioned previously). The incoming request is for /cad and this is internally rewritten to /bbs/board.php?bo_table=cad that actually handles the request.
This can be achieved with a single rule since these 3 URLs follow the same pattern (although that conflicts with the code sample you've posted). For example:
RewriteRule ^(cad|videos|news)$ bbs/board.php?bo_table=$1 [END]
The $1 backreference contains the value of the first capturing group in the RewriteRule pattern. ie. either cad, videos or news.
The external redirect is not strictly necessary, unless you are changing an existing URL structure. Note that the RewriteRule pattern matches against the URL-path only, which notably excludes the query string. (So your rules that include a query string would never match.) To match the query string you need an additional condition (RewriteCond directive) and match against the QUERY_STRING server variable. For example, the following would go before the above rewrite:
RewriteCond %{QUERY_STRING} ^bo_table=(cad|videos|news)$
RewriteRule ^bbs/board\.php$ /%1 [QSD,R=301,L]
Note that we need to use the QSD flag here in order to discard the original query string, we don't want to append it.
The %1 backreference (as opposed to $1) matches the capturing group in the last matched CondPattern (RewriteCond directive).
Don't forget to backslash-escape literal dots in the regex in order to negate their special meaning.
UPDATE:
RewriteRule ^(cad-downloads|cad-videos|news)$ bbs/board.php?bo_table=$1 [END]
To pass cad, videos (video?) or news as the URL parameter, you could do it like this:
RewriteRule ^(?:(cad)-downloads|cad-(videos)|(news))$ bbs/board.php?bo_table=$1 [END]
This is made possible because cad, videos and news are still part of the requested URL. The outer regex group is made non-capturing (with the ?: prefix). An additional capturing group inside this captures the necessary part of the requested URL.
However, the reverse is not possible without hardcoding the mappings.
I'll see if I can get back to your other queries/chat tomorrow...

htaccess rewriterule generating multiple copies of wrong match

Trying to use prettyURLs rewritten to php param qrys using .htaccess rules.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^foo/?([^/]*)/?([^/]*)/?$ /foo.php?s=$1&c=$2 [NC,END,R=301,QSA]
RewriteRule ^bar/?([^/]*)/?$ /bar.php?s=$1 [NC,END,R=301,QSA]
The first rule works correctly, but the second one generates:
https://example.com/bar.php?s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=.php&s=45843
from
https://example.com/bar/45843
RewriteCond directives are only applied to the single RewriteRule immediately following them. That means that your second RewriteRule is not covered by any RewriteCond. Which means it creates an endless rewriting loop.
You want to reject that, pointing out that you rewrite to /bar.php which is not matched again by the matching pattern maybe, but ...
That is not true actually. Take a closer look at your rule:
RewriteRule ^bar/?([^/]*)/?$ /bar.php?s=$1 [NC,END,R=301,QSA]
The matching pattern uses /? which makes the slash optional . So bar.php?s=whatever is again matched. In the next round the rewriting engine does.
Solution:
apply the conditions to both rules and
use a proper matching pattern.
Actually I am not sure what you are trying to match with those patterns ... Why the /??
Are you trying to match a query string that way? That won't work, you need another RewriteCond for that applyiong a matching pattern against %{QUERY_STRING}. That is documented, actually.
Or are you trying to make anything after /bar optional ? Then use a pattern like ^/?bar(/[^/]*)?/?$ maybe ...

htaccess rewrite rule for Forbidding urls

I'm not really new to htaccess rewrites, but today I have seen a rule which I've not seen before:
# Access block for folders
RewriteRule _(?:recycler|temp)_/ - [F]
This rule is part of the Typo3 htaccess file.
What does the "?:" mean? Is this some kind of back reference? And what do the underlines stand for?
Many thanks!
Rule RewriteRule _(?:recycler|temp)_/ - [F] could be divided into 2 rules for better understanding. like:
RewriteRule _recycler_/ - [F]
AND
RewriteRule _temp_/ - [F]
Now let us understand what does that mean:
You could see its a shortcut method to make 1 rule out of 2 rules.
We could use regex to match multiple patterns and perform same kind of action on URIs which are falling in same criteria(which is matched by regex).
In this case we are trying to match _(literal character) followed by (?:recycler|temp). Where ?: stands up for a non-capturing group. So whatever comes into this section(?:.......) will NOT come in backreference capability. Its basically matching string/text recycler OR temp in regex which is preceded and followed by _
Now comes what is capturing group: in .htaccess we can use capability of capture matched values which we can use them later eg--> $1 for getting 1st captured value(stored in memory), we could say non-capturing group tells that we want to match a regex but DO NOT store that into memory(because we DO NOT want to use it later onwards into our program).
Here is an example of capturing group rules in htaccess rules:
RewriteEngine ON
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1.php -f [NC]
RewriteRule ^(first|second)/(.*)?$ $1.php?$2 [QSA,NC,L]
Explanation of above example: Its simply makes 2 capturing groups, 1st will have either first OR second, 2nd capturing group will have anything(because of we used .*) in it, so while rewriting part we are using $1 and $2 to get there values. You could clearly see that we could use these values in condition part as well(which becomes in backend like: %{DOCUMENT_ROOT}/first.php OR %{DOCUMENT_ROOT}/second.php).
Here is an example of non-capturing groups in htaccess Rules:
RewriteEngine ON
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1.php -f [NC]
RewriteRule ^(?:first|second)/(third/fourth)/?$ $1.php [QSA,NC,L]
Explanation of above example: We could see that we are matching first OR second in rule side so now value of $1 will be either third OR fourth this time since we used non-capturing group for first/second. So backend condition check will become like: %{DOCUMENT_ROOT}/third.php OR %{DOCUMENT_ROOT/fourth.php

Remove parentheses from the URLs query string in rewrite rule

I would like to clean up the URL's by removing parentheses from all query strings.
I tried the following code, but couldn't get it to work.
RewriteCond %{REQUEST_URI} [\(\)]+
RewriteRule ^(.*)[\(]+([^\)]*)[\)]+(.*)$ /$1$2$3 [R=301,L]
Here's an example of a URL:
http://www.example.com/blog/abc-post/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+blogname+(Blog+Name+New+York)
In order to match the query string you need to check the QUERY_STRING server variable in a RewriteCond directive.
Here are some ways of doing this:
1. Any number of parentheses - multiple redirects
For example, to remove any number of opening/closing parentheses in the query string part of the URL:
RewriteCond %{QUERY_STRING} (.*)[()]+(.*)
RewriteRule (.*) /$1?%1%2 [R,NE,L]
The NE flag is required in your example to avoid the %-encoded character (ie. %3A) being doubly encoded.
This will, however, result in multiple redirects, depending on the number of "groups" of parentheses. In your example, this will result in two redirects, because there a two "groups" of parentheses (a single parenthesis in each "group").
2. Any number of parentheses pairs - multiple (but fewer) redirects
If the parenthesis are always in matching pairs, then you can specifically check for the opening/closing parenthesis and potentially reduce the number of redirects.
RewriteCond %{QUERY_STRING} (.*)\((.*)\)(.*)
RewriteRule (.*) /$1?%1%2%3 [R,NE,L]
In your example, this results in a single redirect because there is just a single pair of parentheses. But /abc?foo=(bar)&one=(two) would result in two redirects.
3. Any number of parentheses - single redirect
This method performs multiple internal rewrites to remove the parentheses, followed by a single redirect once all the parentheses have been replaced:
# Remove parentheses from query string
RewriteCond %{QUERY_STRING} (.*)[()]+(.*)
RewriteRule (.*) /$1?%1%2 [E=REPLACED_PARENS:1,NE,L]
# Redirect to "clean" URL
RewriteCond %{ENV:REDIRECT_REPLACED_PARENS} 1
RewriteCond %{THE_REQUEST} ^GET\ /(.*)\?
RewriteRule ^ /%1 [R,NE,L]
The first rule internally rewrites the request and sets an environment variable if a replacement is required.
The second rule checks for this environment variable (note that REPLACED_PARENS becomes REDIRECT_REPLACED_PARENS after the first rewrite) and ultimately redirects to the cleaned URL. The URL-path is grabbed from the initial request (contained in the THE_REQUEST server variable) to avoid inadvertantly redirecting to the directory index (eg. index.php) when a bare directory is requested (or front-controller is used).

When to use [L] flag in mod_rewrite

Can someone give me an example when to use [L] flag? I'm learning about mod_rewrite moudle in .htaccess file and can't find out when to this flag.
The L flag simply means to stop applying any rules that follow. Given the same URL, http://example.com/foo/bar?q=blah, and given the rules:
RewriteRule ^foo -
RewriteCond %{REQUEST_URI} !^/bar.php
RewriteRule ^(.*)$ /bar.php?z=$1
The first rule gets applied and the URI gets passed through unchanged (via the - target). The rewrite engine then processes the next rule, and the URI gets rewritten to /bar.php?z=foo/bar. What happens when you add an L to the end:
RewriteRule ^foo - [L]
RewriteCond %{REQUEST_URI} !^/bar.php
RewriteRule ^(.*)$ /bar.php?z=$1
The URL http://example.com/foo/bar gets passed through untouched from the first rule, then stops because of the L flag. If the URL is http://example.com/something/else then the first rule doesn't match and the second rule gets applied, rewriting the URI to: /bar.php?z=something/else
Note that since the rewrite engine loops through all the rules until the URI stops changing, the L flag will not prevent the looping, only any further rules from getting applied in the current iteration.

Resources