I have run into an issue with my .htaccess file.
The file changes the ugly URL such as http://localhost/news.php?article_slug=example-1 to http://localhost/news/example-1
This works perfectly, but when I go to http://localhost/news i get a 404 error.
Within news.php I have a redirect; so if there is not an article slug in the URL it will redirect to latest.php.
my PHP code on news.php
$article_slug=$_GET['article_slug'];
if (empty($_GET)) {
header("Location: ../latest.php");
die();// no data passed by get
}
This is what I currently have in my .htaccsess file
Options -MultiViews
RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
RewriteRule ^news/([\w\d-]+)$ /news.php?article_slug=$1[QSA,L]
RewriteCond %{QUERY_STRING} article_slug=([\w\d-]+)
RewriteRule ^news$ /news/%1 [R,L]
RewriteRule ^category/([\w-]+)$ /category.php?category_slug=$1&page=$2 [QSA]
When I try to debug this myself (with very little knowledge) and add the following line it redirects to latest.php
RewriteRule ^([^/]*)/?$ /news.php [L,QSA]
but on the redirected page I get the following error
The page isn’t redirecting properly
Firefox has detected that the server is redirecting the request for this
address in a way that will never complete.
This problem can sometimes be caused by disabling or refusing to accept cookies
When I use the developer tools in firefox as IMSoP commented all I see is latest.php reloaded multiple times.
This is not just isolated to just latest.php but any file on the server thats not listed in the access file
when I remove the line
RewriteRule ^([^/]*)/?$ /news.php [L,QSA]
I can load the PHP file but it doesn't redirect from news.php and http://localhost/news is not found but http://localhost/news/example-1 works.
but when I go to http://localhost/news i get a 404 error.
None of your rules catch such a request, so no rewriting occurs and you get a 404.
RewriteRule ^([^/]*)/?$ /news.php [L,QSA]
This rewrites everything to /news.php, including /latest.php that you are redirecting to in your PHP script and the cycle repeats, resulting in a redirect loop to /latest.php.
However, this redirect in your PHP code would also seem to assume there is a slash on the original request. ie. should be /news/ (with trailing slash) not /news (no trailing slash) as you state in the question.
It would be better to redirect to a root-relative (or absolute URL) in your PHP script. ie. header('Location: /latest.php');
RewriteRule ^news/([\w\d-]+)$ /news.php?article_slug=$1[QSA,L]
RewriteCond %{QUERY_STRING} article_slug=([\w\d-]+)
RewriteRule ^news$ /news/%1 [R,L]
(Note you are missing a space before the "flags" argument in the first rule.)
The first rule can be modified to allow news/ and not just news/<something>. This is achieved by simply changing the quantifier from + (1 or more) to * (0 or more) on the capturing subpattern.
The second rule is not currently doing anything. You should probably be targeting news.php here. But the rules are also in the wrong order.
As noted in my answer to your earlier question, the \d shorthand character class is not necessary, since \w (word characters) already includes digits.
Try the following instead:
RewriteCond %{QUERY_STRING} (?:^|&)article_slug=([\w-]*)(?:$|&)
RewriteRule ^news\.php$ /news/%1 [R=301,L]
RewriteRule ^news/([\w-]*)$ /news.php?article_slug=$1 [QSA,L]
Note, the first rule should be a 301 (permanent) redirect, not a 302 (as it was initially). But always test first with a 302 to avoid potential cachining issues.
This allows requests to /news/, but not /news (no trailing slash). Only one of these can be canonical. If you need to handle /news as well then you should redirect to append the trailing slash (so /news/ is canonical, and the URL you should always link to.) For example, before the above two rules:
# Append trailing slash if omitted (canonical redirect)
RewriteRule ^news$ /$0/ [R=301,L]
Summary
Bringing the above points together:
Options -MultiViews
RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
# Append trailing slash if omitted (canonical redirect)
RewriteRule ^news$ /$0/ [R=301,L]
RewriteCond %{QUERY_STRING} (?:^|&)article_slug=([\w-]*)(?:$|&)
RewriteRule ^news\.php$ /news/%1 [R=301,L]
RewriteRule ^news/([\w-]*)$ /news.php?article_slug=$1 [QSA,L]
RewriteRule ^category/([\w-]+)$ /category.php?category_slug=$1 [QSA,L]
However, the same now applies to your /category URL. This was also discussed in your earlier question. (I've removed the superfluous &page=$2 part and added the missing L flag.)
If you have many such URLs that follow a similar pattern (eg. news and category etc.) you don't necessarily need a separate rule for each. (An exercise for the reader.)
UPDATE:
$article_slug=$_GET['article_slug'];
if (empty($_GET)) {
header("Location: ../latest.php");
die();// no data passed by get
}
As discussed in comments, this should read:
$article_slug = $_GET['article_slug'] ?? null;
if (empty($article_slug)) {
header("Location: /latest.php");
die(); // no data passed by get
}
Rewrite rules are at heart very simple: they match the requested URL against a pattern, and then define what to do if it matches.
RewriteRule ^([^/]*)/?$ /news.php [L,QSA]
The pattern here translates as "must match right from the start; anything other than a slash, zero or more times; optional slash; must match right to the end". The action if it matches is to act as though the request was for "/news.php", adding on any query string parameters.
That's a very broad pattern; it will match "news" and "news/" but it will also match "hello-world", and "__foo--bar..baz/". The only thing that would stop it matching is other rules higher up your config file.
Meanwhile, every time this rule matches, your PHP code in news.php will run, and if there isn't anything on the query string, will tell the browser to request "latest.php".
But the rule will also match "latest.php". So when the browser requests "latest.php", the code in "news.php" gets run, and tells the browser to request "latest.php" again ... and we have an infinite loop.
The simplest fix is just to make your rule more specific, e.g. look specifically for the word "news":
RewriteRule ^news/?$ /news.php [L,QSA]
Another common technique is to add a condition to the rule that it only matches if the URL doesn't match a real filename, like this:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/]*)/?$ /news.php [L,QSA]
Related
I'm struggling with my .htaccess file and setting it up the way I want it. The main function is a website that gets the language from the subdomain and the current page from the subfolders.
Requirements
I have three requirements that I need my .htaccess file to do;
Wildcard subdomain redirected to lang variable
Subfolder(s) redirected to page variable
Local files respected (this is where I'm stuck)
(Bonus) Split up the page variable into segments for each slash; page, sub1, sub2, etc
Examples
en.example.com/hello -> /index.php?lang=en&page=hello
es.example.com/hola -> /index.php?lang=es&page=hola
(Bonus) en.example.com/hello/there/sir -> index.php?lang=en&page=hello&sub1=there&sub2=sir
My current .htaccess
This is my current setup which actually kinda works, if I don't need any local files (lol). This means local images aren't found when my .htaccess below is active. I tried adding RewriteCond %{REQUEST_FILENAME} !-f to respect local files but that breaks the whole file it seems - and I don't know why.
RewriteCond %{REQUEST_URI} ^/$
RewriteCond %{HTTP_HOST} ((?!www).+)\.example\.com [NC]
RewriteRule ^$ /index.php?lang=%1 [L]
RewriteCond %{HTTP_HOST} ((?!www).+)\.example\.com [NC]
RewriteRule ^(.+)$ /index.php?lang=%1&page=$1 [L]
RewriteRule ^index\.php$ - [L]
RewriteRule ^(.*)$ /index.php?page=$1 [L,QSA]
If your URLs don't contain dots then exclude dots from your regex - this naturally excludes real files (that contain a dot before the file extension). This avoids the need for a filesystem check.
Your script should handle /index.php?lang=%1 and /index.php?lang=%1&page= exactly the same, so the first rule is superfluous.
RewriteRule ^index\.php$ - [L]
This rule should be first, not embedded in the middle.
Try the following instead:
RewriteRule ^index\.php$ - [L]
RewriteCond %{HTTP_HOST} ^((?!www).+)\.example\.com [NC]
RewriteRule ^([^.]*)$ /index.php?lang=%1&page=$1 [QSA,L]
RewriteRule ^([^.]*)$ /index.php?page=$1 [QSA,L]
Your last rule that rewrites everything else to index.php, less the lang URL param is questionable. Why not just include this in the preceding rule and validate the language in your script? Which you need to do anyway.
Assuming there is always a subdomain, then your rules could then be reduced to:
RewriteRule ^index\.php$ - [L]
RewriteCond %{HTTP_HOST} ^(.+)\.example\.com [NC]
RewriteRule ^([^.]*)$ /index.php?lang=%1&page=$1 [QSA,L]
Requests for the www language are then validated by your script and defaulted accordingly, as if the lang param was not passed at all (which you need to be doing anyway).
If your subdomain is entirely optional and you are accessing the domain apex then make it optional (with a non-capturing group) in the regex:
RewriteCond %{HTTP_HOST} ^(?:(.+)\.)?example\.com [NC]
:
The lang param would then be empty if the domain apex was requested.
(Bonus) en.domain.com/hello/there/sir -> index.php?lang=en&page=hello&sub1=there&sub2=sir
It would be preferable (more efficient, flexible, etc) to do this in your PHP script, not .htaccess.
But in .htaccess you could do something like this (instead of the existing rule):
:
RewriteRule ^([^/.]*)(?:/([^/.]+))?(?:/([^/.]+))?(?:/([^/.]+))?(?:/([^/.]+))?$ /index.php?lang=%1&page=$1&sub1=$2&sub2=$3&sub3=$4&sub4=$5 [QSA,L]
The URL params are empty when that path segment is not present.
It is assumed the URL-path does not end in a slash (the above will not match if it does, so a 404 will result). If a trailing slash needs to be permitted then this should be implemented as a canonical redirect to remove the trailing slash. Or reverse the logic to enforce a trailing slash.
This particular example allows up to 4 additional "sub" path segments, eg. hello/1/2/3/4. You can extend this method to allow up to 8 (since there is a limit of 9 backreferences in the Apache syntax) if required. Any more and you will need to use PHP. (You could potentially handle more using .htaccess, but it will get very messy as you will need to employ additional conditions to capture subsequent path segments.)
I tried adding RewriteCond %{REQUEST_FILENAME} !-f to respect local files but that breaks the whole file it seems
That should also be sufficient (if dots are permitted in your URLs). But I wonder where you were putting it? It should not "break" anything - it simply prevents the rule from being processed if the request does map to a file - the rule is "ignored".
This is of course assuming you are correctly linking to your resources/static assets using root-relative (starting with a slash) or absolute (starting with scheme + hostname) URLs. If you are using relative URLs then they will probably result in 404s. If this is the case then see my answer to the following question on the Webmasters stack:
https://webmasters.stackexchange.com/questions/86450/htaccess-rewrite-url-leads-to-missing-css
Trying to use .htaccess rule to do the wp-login JS check on first visit by appending ?/wp-login to the url since it's interferring with Sucuri firewall when using password protection.
I've created a test subdomain to try to get the htaccess redirect to work before using it on the live site:
RewriteCond %{QUERY_STRING} ^protectedpage$
RewriteRule ^(.*)$ https://testing.no11.ee/protectedpage?/wp-login [R=302,L]
view here: testing.no11.ee/protectedpage
Unfortunately this does not add the query arg to the url. What am I doing wrong here?
Expected result when visiting page should be https://testing.no11.ee/protectedpage?/wp-login as the browser url.
Full htaccess:
# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
# Any changes to the directives between these markers will be overwritten.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
RewriteCond %{QUERY_STRING} ^protectedpage$
RewriteRule ^(.*)$ https://testing.no11.ee/protectedpage?/wp-login [R=302,L]
</IfModule>
# END WordPress
RewriteCond %{QUERY_STRING} ^protectedpage$
RewriteRule ^(.*)$ https://testing.no11.ee/protectedpage?/wp-login [R=302,L]
This checks that the QUERY_STRING is set to protectedpage, but in your example it's the URL-path that is /protectedpage, not the query string.
You also need to first check that the query string is not already set to /wp-login, otherwise you'll get a redirect loop.
However, you've also put the code in the wrong place. Note the WordPress comment that precedes the code block - you should not manually edit this code. This directive also needs to go before the WordPress front-controller, otherwise, it's simply never going to get processed.
Try the following instead before the # BEGIN WordPress comment marker:
RewriteCond %{QUERY_STRING} !^/wp-login$
RewriteRule ^(protectedpage)/?$ /$1/?/wp-login [R=302,L]
This matches an optional trailing slash on the requested URL, but it redirects to include the trailing slash in the target URL.
(You do not need to repeat the RewriteEngine on directive.)
No need to include the scheme + hostname if you are redirecting to the same. The $1 backreference simply saves repetition and refers to the matched URL-path, ie. protectedpage (without the trailing slash) in this example.
However, this always redirects and appends /wp-login to this URL - not just the "first visit" - is that really what you require? Otherwise, you need to somehow differentiate between "first" and "subsequent" visits (by detecting a cookie perhaps?)
UPDATE: Minor addition: how would one improve this to add ?/wp-login to all urls that have the page /subpage/ as parent i.e /subpage/page-1 and /subpage/page-2 would result in /subpage/page-1?/wp-login etc? I tried using (.*) but this delets the subpage from the url...
You could do something like the following:
RewriteCond %{QUERY_STRING} !^/wp-login$
RewriteRule ^(subpage/[^/]+)/?$ /$1/?/wp-login [R=302,L]
The [^/]+ subpattern matches any character except / - so only the second path segment, excluding the optional trailing slash. This is similar to .*, but this would capture everything, including any trailing slash, so would result in a double slash in the redirected URL.
I'm trying to figure out how to rewrite urls using apache webserver and php.
The url below is the real nonrewritten url:
http://localhost:1337/rewritetest/index.php?id=12
And I want to reach it by
http://localhost:1337/rewritetest/index/12
My indexfile looks like this:
<?php
echo $_GET['id'];
?>
Is this possible? The "new" url doesn't include any parameter names so I guess I have to use an order of parameters instead but I dont know how to reach them in that case.
Below is as far I've come with my rewrite:
RewriteCond %{QUERY_STRING} id=([-a-zA-Z0-9_+]+)
RewriteRule ^/?index.php$ %1? [R=301,L]
RewriteRule ^/?([-a-zA-Z0-9_+]+)$ index.php?id=$1 [L]
Anyone have an idea of what I'm doing wrong?
it's located in the same folder as index.php
So, given the .htaccess file is located at /rewritetest/.htaccess (as opposed to the document root ie. /.htaccess) then...
RewriteRule ^/?([-a-zA-Z0-9_+]+)$ index.php?id=$1 [L]
If you request a URL of the form /rewritetest/index/12 then the above RewriteRule pattern won't actually match anything. It tries to match "index/12", but your pattern does not contain a slash so will fail. (Is the + inside the character class intentional?)
Try something like the following instead:
RewriteRule ^(index)/(\d+)$ $1.php?id=$2 [L]
This obviously specifically matches "index" in the URL. If you are always rewriting to index.php then you don't really need "index" in the URL - unless this means something different? This also assumes that the valuue of the id parameter consists only of digits.
To rewrite the more general .../<controller>/26 to .../<controller>.php?id=26 (as mentioned comments) then try something like:
RewriteRule ^([\w-]+)/(\d+)$ $1.php?id=$2 [L]
In per-directory .htaccess files the slash prefix is omitted on the URL-path that is matched by the RewriteRule pattern, so /? is not required. The above pattern also matches something for for the id, not anything. So, /index/ would not match.
If this is a new site then the "redirect" (from /index.php?id=12 back to /index/12) is not necessarily required. That's only really required if you are changing the URL structure on an existing site where old URLs already have inbound links and are indexed by search engines. In which case you could do something like the following before the internal rewrite:
RewriteBase /rewritetest/
RewriteRule %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^id=(\d+)
RewriteRule ^(index)\.php$ $1/%1 [R,L]
Or, for a more generic .../<controller>/26 to .../<controller>.php?id=26 (as above) then change the RewriteRule to:
RewriteRule ^([\w-]+)\.php$ $1/%1 [R,L]
The additional check against the REDIRECT_STATUS environment variable is to prevent a rewrite loop after having rewritten the URL to /index.php?id=12 earlier.
I am redirecting all requests to a specific folder by htaccess. Its is working perfect for me. I need to block the redirection on two conditions.
mysite.com/admin
and when it is empty like
mysite.com/
By below code the redirect and blocking on admin happens.
RewriteCond %{REQUEST_URI} !^/admin/
RewriteRule ^(.*)$ /com1/$1 [L]
You may exit the rule chain early, by not rewriting at all, see RewriteRule Substitution
The Substitution of a rewrite rule is the string that replaces the original URL-path that was matched by Pattern. The Substitution may be a:
...
- (dash)
A dash indicates that no substitution should be performed (the existing path is passed through untouched). This is used when a flag (see below) needs to be applied without changing the path.
and the L|last flag.
RewriteRule ^admin - [L]
RewriteRule ^$ - [L]
RewriteRule !/com1/ /com1%{REQUEST_URI} [L]
Im redirecting this:
example.com/category/name-of-title/number
to this:
example.com/name-of-title/number
Everything is working perfect except its adding %20HTTP to the end of the url after the redirect like so:
example.com/name-of-title/number%20HTTP
Here is my code:
RewriteCond %{THE_REQUEST} /category/(.*)/(.*) [NC]
RewriteRule ^ /%1? [R=301,L]
Because as the documentation states, THE_REQUEST is
“[t]he full HTTP request line sent by the browser to the server (e.g., "GET /index.html HTTP/1.1")”
– so in your case, that would be something like GET /category/name-of-title/number HTTP/1.1, and applying the pattern /category/(.*)/(.*) onto that results in exactly the part name-of-title/number HTTP as the first match (and the remaining 1.1 as the second), because of the greediness of regular expressions.
It is nonsense anyway to use a RewriteCond and the value THE_REQUEST for this – a simple RewriteRule like this should work perfectly fine:
RewriteRule ^category/(.*)/(.*)$ /$1/$2 [R=301,L]
And if you want to match anything that could be behind category/ anyway, you don’t even need two capturing subpatterns, but could simply use
RewriteRule ^category/(.*)$ /$1 [R=301,L]
%20http is an empty space!
Remove the space, and this %20http will no longer appear.