How to use SetEnv with a URL parameter - apache

I'm trying to implement language switching in .htaccess, and the only thing left now is to handle clients which don't support cookies. To do that, I must set prefer-language when the user clicks a link with a language parameter.
RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} (?:^|&)language=(en|fr|no)
RewriteRule ^(.*)$ $1? [cookie=language:%1:.example.com,env=language:%1,R]
SetEnv prefer-language $language
The problem is with the last line - The value is always set to empty. It works if I hardcode it, but not if I try to refer to a variable. Is there some special syntax to refer to environment variables in this context, or is there some other way to set prefer-language?
Edit: Cross-posted to Apache users list.

You can set environment variables with mod_rewrite as well. Actually, you already did that (see env/E flag).
I can’t test it with mod_negotiation myself, but the following should work and set the prefer-language:
RewriteCond %{QUERY_STRING} ^((?:[^&]&)*)language=(en|fr|no)&?([^&].*)?$
RewriteRule ^ %{REQUEST_URI}?%1%3 [L,CO=language:%2,R]
RewriteCond %{HTTP_COOKIE} (^|[,\s])language=([^\s,;]+)
RewriteRule ^ - [L,E=prefer-language:%2]
SetEnvIf REDIRECT_prefer-language (.+) prefer-language=$1
But it would be far easier if you put the language identifier into the URL path like /en/…:
SetEnvIf Request_URI ^/(en|fr|no)/ prefer-language=$1
SetEnvIf REDIRECT_prefer-language (.+) prefer-language=$1
I don’t know if you need the additional/second SetEnvIf variable.

Looks like there's no support for variables in SetEnv, but here's a working configuration if someone else is trying to do the same. It's a simpler kind of language selection, since it just copies the language parameter from the referer to the current URL if it's not changed:
RewriteEngine On
RewriteBase /
# Keep the language parameter if specified in the last URL
RewriteCond %{HTTP_REFERER} ^(?:.*[&?])?language=(en|fr|no).*$
RewriteCond %{QUERY_STRING} !^(?:.*&)?language=(en|fr|no).*$
RewriteRule ^(.*)$ $1?language=%1 [redirect=permanent]
# Set the language from the URL parameter
RewriteCond %{QUERY_STRING} ^(?:.*&)?language=(en|fr|no).*$
RewriteRule ^ - [env=prefer-language:%1]
# Cache only when the language parameter is set
<IfDefine !prefer-language>
Header set Vary *
</IfDefine>

Related

How to add a header if the url contain any get parameter using htaccess file?

I am running PHP in Apache2 server. I would like to add header("X-Robots-Tag: noindex", true); to my pages if the url contains any get parameters like https://example.com/? or https://example.com/a.php? or https://example.com/a.php?key=value. So basically, if the url contain ? charter, the header needs to be added.
Is it possible to do it using .htaccess file and how to do it? Or is there any other way to achieve this?
Ref: https://yoast.com/x-robots-tag-play/
EDIT
Below are the existing rewrite codes;
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^nps-benchmarks/(.*)$ http://testing.mysite.com.s3-website-us-east-1.amazonaws.com/$1 [P]
RewriteRule ^agency/(.*)$ http://testing.mysite.com.s3-website-us-east-1.amazonaws.com/site/mod1/$1 [P]
RewriteRule ^apps/(.*)$ http://testing.mysite.com.s3-website-us-east-1.amazonaws.com/site/mod2/$1 [P]
RewriteRule ^help-each-other/(.*)$ http://testing.mysite.com.s3-website-us-east-1.amazonaws.com/site/mod3/$1 [P]
RewriteRule ^customers-help-each-other/(.*)$ http://testing.mysite.com.s3-website-us-east-1.amazonaws.com/site/mod4/$1 [P]
RewriteRule ^de/(.*)$ http://testing.mysite.com.s3-website-us-east-1.amazonaws.com/site/multilanguage/de/$1 [P]
RewriteRule ^es/(.*)$ http://testing.mysite.com.s3-website-us-east-1.amazonaws.com/site/multilanguage/es/$1 [P]
RewriteRule ^fr/(.*)$ http://testing.mysite.com.s3-website-us-east-1.amazonaws.com/site/multilanguage/fr/$1 [P]
RewriteRule ^(wp-content/themes/my-site/assets/)(css|js)(/.+)(-v\d+\.\d+)\.(min)\.(js|css)$ $1/$2/$3.$5.$6 [L]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
like https://example.com/? or https://example.com/a.php?
The issue in these two instances are that the query string is empty, so you can't check for this using the standard QUERY_STRING server variable ("empty" and "not-exists" evaluate to the same thing, ie. empty).
However, you can check for a literal ? (ie. query string delimiter) in the first line of the HTTP request headers, as contained in the THE_REQUEST Apache server variable.
In the case of the first example above, this will contain a string of the form:
GET /? HTTP/1.1
And for /a.php?key=value, this will be:
GET /a.php?key=value HTTP/1.1
We can use mod_rewrite to check this and set an environment variable. Then use the header directive to set the required X-Robots-Tag HTTP response header conditionally based on whether this env var is set.
For example, near the top of the root .htaccess file:
RewriteEngine On
# Check for literal "?" in URL and set QUERY_EXISTS env var
RewriteCond %{THE_REQUEST} \s/.*\?
RewriteRule ^ - [E=QUERY_EXISTS:1]
# Set header if QUERY_EXISTS is set
header set X-Robots-Tag "noindex" env=QUERY_EXISTS
This sets the header on 2xx "OK" responses. It won't, for instance, set the header on a 404 - but 404s are not indexed anyway, so the header is redundant. However, if you specifically need to set the header on all (non-2xx) responses then use the always condition. For example:
# Use "always" to set on non-2xx responses as well
header always set ....
Note that this is also dependent on other directives you might have in your .htaccess file. For example, if you have existing mod_rewrite directives that cause the rewrite engine to "loop" then the env var will be renamed to REDIRECT_QUERY_EXISTS and you will likely need to check for this instead in the header directive.

how to compare the current time to a variable in a .htaccess

I would like to make a redirection after a deadline on my site.
currently everything works correctly if I write this :
RewriteEngine On
SetEnv DEADLINE 16/06/2022_19h30
RewriteCond %{TIME_DAY}/%{TIME_MON}/%{TIME_YEAR}_%{TIME_HOUR}h%{TIME_MIN} >=16/06/2022_19h30
RewriteRule ^index.php$ read_only/index.html [L]
the advantage of using an environment variable is that I can use it elsewhere in a PHP file
So is there a way to test between the current time and the DEADLINE variable?
You can do this but you need to set your variable using SetEnvIf directive, so that it is available for evaluation in .htaccess RewriteCond:
Define this variable first:
SetEnvIf Host ^ DEADLINE=20220616193000
Then use it in .htaccess using RewriteCond expressions:
RewriteEngine On
RewriteCond expr "%{TIME} -ge env('DEADLINE')"
RewriteRule ^index\.php$ read_only/index.html [L,NC]
Due to use of expr in RewriteCond, it will require Apache 2.4+

How to write an IF contains condition matching 2 ENV variables set via RewriteRules

To give context, the goal:
Using apache httpd I need to test a contextual segment of the request uri is contained within a cookie value sent in the header before redirect the user to a splash page for cookie confirmation, or allowing them access to the site.
Given the following:
url: http://www.example.com/en/gb/main.html
cookie: ;acceptedCookies=fr,en,de,it
If the language segment of the url = "en", and this is amongst the users acceptedCookies. Then allow the user access to the main site, else redirect the user to the splash page.
What I've tried:
SetEnvIf Host ^ uriCountryCode=NUL
SetEnvIfNoCase Request_URI "^(.+)?\/([a-zA-Z]{2})\/[a-zA-Z]{2}\/.+$" uriCountryCode=$2
SetEnvIf Host ^ acceptedCookieList=EMP
SetEnvIf Cookie "(^|;\*)acceptedCookies=([^;]+)?" acceptedCookieList=$2
#1st.
<If "%{acceptedCookieList} =~ %{uriCountryCode}">
// Doesn't work
</If>
#2nd.
<If "env('acceptedCookieList') =~ env('uriCountryCode')">
// No luck
</If>
#3rd.
<If "env('acceptedCookieList') =~ /env('uriCountryCode')/">
// Now I'm just guessing
</If>
#4th.
RewriteCond %{acceptedCookieList} !%{uriCountryCode}
RewriteCond %{REQUEST_URI} !^/en/gb/splash.html$ [NC]
RewriteRule ^(.+)$ /mt/en/splash [L,R=301]
I've recently learnt that some of these modules i.e env('x') won't load SetEnvIf variables dues to the SetEnvIf variables not loading early enough to be accessible but i'm trying to find a httpd solution to this my problem
mod_rewrite can set environment variables.
Use a 302 redirect (Moved Temporarily) instead of a 301 redirect (Moved Permanently).
Matching against an arbitrary regex provided by the client (i.e. the contents of the acceptedCookies cookie) is dangerous. Nevertheless, the httpd expressions language doesn't have a built-in "contains" function and so it's non-obvious how to avoid the regex matching. This will make your server(s) susceptible to DoS attacks.
All that said, consider something like:
RewriteCond %{HTTP_COOKIE} acceptedCookies=([^;]+)
RewriteRule ^.*$ - [E=COOKIE_ACCEPTED_LANGS:%1]
RewriteCond %{REQUEST_URI} ^/([^/]+)/.*$
RewriteRule ^.*$ - [E=URL_LANG:%1]
RewriteCond expr %{ENV:URL_LANG} =~ %{ENV:COOKIE_ACCEPTED_LANGS}
RewriteCond %{REQUEST_URI} !^/en/gb/splash.html$ [NC]
RewriteRule ^.*$ /mt/en/splash [R]
Following #Avi solution will work, but any solution allowing client-side values unknown in length to perform validation is fundamentally flawed, posing the potential risk of exceeding buffers and DOS.

REQUEST_URI rewrite incl. query string

I have a WordPress plug-in and theme that are malforming a URL call to an external css file. I will fix that later, but until then, I need to do a redirect so the css is getting served correctly.
REQUEST_URI contains the full path, so why doesn't this work?
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^/?wp-content/plugins/jetpack/css/%22https:/fonts.googleapis.com/css?family=Open+Sans%22$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/?wp-content/plugins/jetpack/css/"https:/fonts.googleapis.com/css?family=Open+Sans"$
RewriteRule ^(.*)$ https://fonts.googleapis.com/css?family=Open+Sans [R,L]
</IfModule>
Note 1: I have tried several combinations of escaping characters in the RewriteCond. Nothing works.
Note 2: I am using flag [R,L] for testing, to prevent browser caching. Will change to [R=301,L] later.
Note 3: I have tested to verify that the problem is with the RewriteCond, not the RewriteRule.
UPDATE:
OK, so this was a bad question and a dumb mistake. Even though raw REQUEST_URI contains the query string, mod_rewrite specifically does not allow query string to be in REQUEST_URI for matching purposes. Hence, QUERY_STRING instead. I thought that it could be done either way.
I don't have a true query string in my REQUEST_URI -- I have the query string that is in a string that I wanted to match -- so that is why I didn't go to QUERY_STRING at the outset. But mod_rewrite still sees the ? as a typical query string start.
Sorry, guys. I can handle it like this until I fix the underlying problem in WordPress:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^/?wp-content/plugins/jetpack/css/%22https:/fonts.googleapis.com/css(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/?wp-content/plugins/jetpack/css/"https:/fonts.googleapis.com/css(.*)$
RewriteRule ^(.*)$ https://fonts.googleapis.com/css?family=Open+Sans [R,L]
</IfModule>
With WordPress, always remember to put your rewrites above the WordPress rewrites.

Domain name in mod_rewrite RewriteRule

The task is to get referer domain and send it to my script by RewriteRule. My decision is
SetEnvIf Referer "^https?://(.*)/" myref=$1
RewriteRule ^(.*)$ script.php?referer=%{ENV:myref}
It works right but I wonder if there are any ways to do it (perhaps with RewriteCond)?
mod_rewrite has a variable named %{HTTP_REFERER}. It contains what you expect. You can use it like this:
RewriteCond %{REQUEST_URI} !^/script\.php$
RewriteRule ^ script.php?referer=%{HTTP_REFERER} [L]
See the documentation for more information.