I know that there are many post in here about RewriteRule regex question, but my one is difference.
I follow this tutorial (https://www.visualscope.com/seo-friendly-urls.html) to make a seo friendly url and it work except when user change language.
URL (this works):
/wedding-venue-details/abc-hotel/ -> /wedding-venue-details/?seoURL=abc-hotel
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^wedding-venue-details/(.*)$ ./wedding-venue-details/?seoURL=$1
</IfModule>
but if the user tries to change the language in this page, the URL will become:
/wedding-venue-details/abc-hotel/?lang=en
I just don't know how to replace ?lang=en to &lang=en and append to the end, I tried but still can't get it work.
RewriteRule ^wedding-venue-details/(.*)$ ./wedding-venue-details/?seoURL=$1
You need to use the QSA (Query String Append) flag to append the lang=en parameter onto the end of the query string on the rewritten URL. Apache then handles the merging of the URL parameters and correctly formats the query string.
You are also missing the L flag (important if you add any more directives).
For example:
RewriteRule ^wedding-venue-details/(.*) wedding-venue-details/?seoURL=$1 [QSA,L]
You should remove the ./ prefix from the substitution string (it has no place here - get gets resolved away later).
This will rewrite /wedding-venue-details/abc-hotel/?lang=en to /wedding-venue-details/?seoURL=abc-hotel/&lang=en. Note that, despite your example stating otherwise, this will copy the trailing slash to the seoURL parameter value.
Additional:
Although /wedding-venue-details/?seoURL=abc-hotel/ isn't strictly a valid end-point. This is still reliant on other Apache modules/directives to route the request to a file (or "front-controller") eg. index.html (or index.php) to actually handle the request. For instance, this should probably be /wedding-venue-details/index.html?seoURL=abc-hotel/ (or /wedding-venue-details/index.php?seoURL=abc-hotel/ if using PHP).
Related
I am trying to redirect from an url to another url but keeping the queries.
For example, from
/oldurl?query1=yes&query2=yes&... (or any list of queries)
to
/newurl?fixedquery=yes&query1=yes&query2=yes&...
So in pratice it would redirect the old url and its queries to a new url, keeping the old queries, plus a fixed query.
This is what I have been trying to use (unsuccessfully) in the .htaccess:
RedirectMatch 301 /oldurl/?$ newurl/?fixedquery=yes&$1
I also tried before using Rewrite
RewriteCond %{QUERY_STRING} ^fixedquery=yes$
RewriteRule ^oldurl/?$ newurl/? [R=301,L]
But this simply redirects if for /oldurl (adding fixedquery) and gives a 404 in case I pass a query to oldurl (e.g. /oldurl?var1=1).
Where am I wrong?
You need to use mod_rewrite in order to manipulate the query string. Try the following instead:
RewriteRule ^oldurl/?$ /newurl?fixedquery=yes [QSA,R=301,L]
Your example URL omits the trailing slash on the target URL, so I omitted it here. However, in your directives, you are including it?
The QSA flag appends/merges the original query string from the request. So the resulting URL is /newurl?fixedquery=yes&query1=yes&query2=yes&..., where query1=yes&query2=yes&... were passed on the initial request.
UPDATE: Note that this rule needs to go near the top of the file, before any existing rewrites. The order of directives in the .htaccess file can be important. Test first with 302 (temporary) redirects to avoid potential caching issues. And you will need to ensure the browser cache is cleared before testing.
A look at your attempts...
RedirectMatch 301 /oldurl/?$ newurl/?fixedquery=yes&$1
The RedirectMatch (mod_alias) directive matches against the URL-path only, but you have no capturing subgroup in the regex, so the $1 backreference is always empty.
RewriteCond %{QUERY_STRING} ^fixedquery=yes$
RewriteRule ^oldurl/?$ newurl/? [R=301,L]
This matches a request for /oldurl?fixedquery=yes and redirects to newurl/ - removing the query string entirely. However, this is also reliant on RewriteBase being set, otherwise this will result in a malformed redirect, exposing your directory structure.
We have a htaccess rule like this:
RewriteRule ^(.*)/(.*)/(.*) ../app$1/scripts/api/index.php?fn=$2&$3 [L]
This works fine in most cases, however, Apache decodes the url before it arrives at this rule, so a url like beta/list/&cat=red%20%26%20blue, is seen by htaccess as beta/list/&cat=red & blue so we get cat='red' and blue=null coming into index.php instead of cat='red & blue'.
I've read that the workaround for this issue is to use server variables like %{REQUEST_URI} %{THE_REQUEST} in the htaccess rule as these are not decoded before use, but it's difficult to implement. The question mark in the RewriteRule makes everything go crazy and I can't figure out how to escape it.
Can any experts out there help me fix the rule below to behave like the one above?
RewriteCond %{REQUEST_URI} ^(.*)/(.*)/(.*)
RewriteRule . ../app%1/scripts/api/index.php?fn=%2&%3 [L]
Indeed, the solution is to use the special server-variable called THE_REQUEST.
From mod_rewrite documentation:
THE_REQUEST
The full HTTP request line sent by the browser to the server (e.g.,
"GET /index.html HTTP/1.1"). This does not include any additional
headers sent by the browser. This value has not been unescaped
(decoded), unlike most other variables below.
Here is how your rule should look like
# don't touch urls ending by index.php
RewriteRule index\.php$ - [L]
# user request matching /xxx/xxx/xxx (with optional query string)
RewriteCond %{THE_REQUEST} \s/([^/\?]+)/([^/\?]+)/([^\?]+)(?:\s|\?) [NC]
RewriteRule ^ ../app%1/scripts/api/index.php?fn=%2&%3 [L,QSA]
Please note that you shouldn't be using relative path for internal rewrite, which could lead to confusion. Instead, define a RewriteBase, use an absolute path or start from the domain root with a /.
UPDATE
Since you can have encoded forward slashes in your url, you need to set AllowEncodedSlashes to NoDecode (or On but it's unsafe). Note also that, due to a bug, you must put this directive inside a virtual host context, even if the server config context is said to be OK (otherwise, it is simply ignored). By default, AllowEncodedSlashes is set to Off. So, Apache handles encoded slashes automatically by itself and refuses them, without passing the request to mod_rewrite. See the official documentation here.
Goal: Want to rewrite all URLs of type
https://www.example.com/page/1234/?/blog/foo/bar/
to
https://www.example.com/page/1234/
In .htaccess I tried many variations along the line
RewriteEngine On
RewriteBase /
RewriteRule ^page/(\d+)/(.*)$ /page/$1 [R=301,L]
Using an .htaccess tester I see that at least the matching pattern is valid.
I would expect that the rewrite would not include anything after $1, but it does, and show the complete original URL.
What am I missing?
https://www.mypage.com/page/1234/?/blog/foo/bar/
Everything after the first ? is the query string part of the URL. By default, Apache passes the query string unaltered from the request to the target URL (unless you create a new query string yourself on the RewriteRule substitution). This explains why you are seeing the same query string on the target URL, without seemingly doing anything with it.
Incidentally, the RewriteRule pattern only matches against the URL-path only - this notably excludes the query string. To match the query string in mod_rewrite you need an additional condition that checks the QUERY_STRING server variable.
On Apache 2.4+ you can use the QSD (Query String Discard) flag to remove the query string from the target URL. Or, specify an empty query string on the substitution - by including a trailing ? (the ? itself does not appear on the resulting URL).
For example (on Apache 2.4):
RewriteCond %{QUERY_STRING} .
RewriteRule ^page/(\d+)/ /page/$1/ [QSD,R=301,L]
The RewriteCond directive checks for the presence of a query string, which is necessary to prevent a redirect loop.
The trailing (.*)$ on the RewriteRule pattern was superfluous.
You had omitted the trailing slash on the end of the substitution (that is present on the example URL). This would have also prevented a redirect loop, but as mentioned, this is not as per your example. (Alternatively, you could include the slash in the captured backreference.)
If you are still on Apache 2.2 then you would need to include a trailing ? instead of the QSD flag. For example:
RewriteRule ^page/(\d+)/ /page/$1/? [R=301,L]
You will need to clear your browser cache before testing, as 301 (permanent) redirects are cached persistently by the browser. For this reason, it is often easier to first test with 302 (temporary) redirects.
Possible this question has already been answered but I didn't find any answer after hours of searching.
I need to put the site under "maintenance mode" and redirect/rewrite all requests to site_down.html, but at the same time I need the site to be available if I enter the address like files are in a subfolder.
ex:
if I type http://example.com/login.php I need site_down.html to be displayed.
but if I specify http://example.com/test/login.php I need real login.php do be displayed.
I need this to be done with rewrite, so copying everything to another directory isn't a solution.
I tried a couple dozens of combinations, but I'm still unable to achieve what I need
This is one version of my .htaccess file ():
DirectoryIndex site_down.html
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^test\/(.*)$ $1 [S=1]
RewriteRule ^(.*\.php)$ site_down.html
RewriteRule .* - [L]
</IfModule>
This code should rewrite all requests with "test/*" to "parent folder" and skip next rewrite rule and then terminate rewriting at RewriteRule .* - [L]. If there is no "test/" in url - all request should be rewritten to site_down.html
What am I doing wrong?
Could you suggest any valid solutions, please?
Thank you.
Essentially, you are searching for 2 rules. One rule will translate a virtual subdirectory to the working files. The other rule will translate the url to the working files to a splash page. We just have to make sure that if the first rule matches, the second rule doesn't match. We can do this by making sure " /test/" (including that leading space) was not in THE_REQUEST (or the string that the client sent to the server to request a page; something in the form of GET /test/mypage.php?apes=bananas HTTP/1.1). THE_REQUEST doesn't change on a rewrite, which makes it perfect for that. Skipping a rule like you did usually doesn't have the effect you expect, because mod_rewrite makes multiple passes through .htaccess until the resulting url doesn't change anymore, or it hits a limit and throws an error. The first time it will skip the rule, but the second time it will not do that.
RewriteCond %{THE_REQUEST} !\ /test/
RewriteRule \.php site_down.html [L]
RewriteRule ^test/(.*)$ $1 [L]
I want to use a rather simple rewrite, something like this:
RewriteRule monitor.html index.php/\?first_category_id=B008 [NC,L]
But it doesn't work as expected, goes to like index.php/monitor.html (which kicks in symfony's routing and returns a 404 error but this is a different story)
However if i include full url like:
RewriteRule monitor.html http://example.com/index.php/\?first_category_id=B008 [NC,L]
it responses the correct content, but this looks like a full redirect, the rewrited url is revealed in the browser. And thats not transparent nor easily deployable.
What am i missing here?
the rest of the htaccess file if it matters:
RewriteCond %{REQUEST_URI} \..+$
RewriteRule .* - [L]
RewriteRule ^(.*)$ index.php [QSA,L]
Your rule is outputting a relative path and you're in a per-directory context. You need RewriteBase. In a per-directory context, rewriting is being done on expanded filesystem paths, not on the original URL's. But the results of the expansion are converted to a URL again! RewriteBase supplies the prefix needed to do that. Without it, the URL is naively made out of the same filesystem prefix that was stripped prior to the substitution and you end up with for instance http://example.com/var/www/docroot/blah... which is nonsense. Either RewriteBase or put out an absolute, beginning with a slash.
Also, you should anchor the match:
RewriteRule ^monitor.html$ ...
Otherwise the rule will potentially match somewhere in the middle of the path and just that matching part will be replaced with the substitution! You don't want to match and translate amonitor.htmly/foobar, right, and convert just the monitor.html part to a the index.php stuff.
You should not escape the question mark in the substitution. It's not a regexp! Just index.php/?etc not index.php/\?etc (Could that backslash be what is screwing up, causing `index.php/monitor.html'?)