2

I want to set a header in my Apache (2.4) config, but only if an environment variable has a certain value.

SetEnv ENV_NAME prod

How do I only set this header if ENV_NAME is not prod ?

Header set X-Robots-Tag "noindex, nofollow"

2 Answers 2

2
Header set X-Robots-Tag "noindex, nofollow" "expr=env('ENV_NAME') != 'prod'"

From https://httpd.apache.org/docs/2.4/mod/mod_headers.html#header

The Header directive may be followed by an additional argument, which may be any of:

...

expr=expression The directive is applied if and only if expression evaluates to true. Details of expression syntax and evaluation are documented in the ap_expr documentation.

Sign up to request clarification or add additional context in comments.

Comments

0

I also give my solution, for it took me some long hours to succeed in something wich looked so simple to me.
I wanted to write a kind of htaccess template for the WordPress sites I build for customers, where I would just have to set values to some variables and the template just does the rest – especially everything which concerns the CSP headers.
I didn't post the full template (for it takes about 250 lines of other complicated code) and was amazed to see how brain-damaging Apache syntax can be for a so simple problem !!!
set some vars; if (var = this) { do this } else { do that }

It took me time to find the right syntax, having to test combinations with SetEnv, SetEnvIf, If/Else, %{ENV:MY_VAR}, %{MY_VAR}e, '${MY_VAR}', env('MY_VAR'), reqenv('MY_VAR') (and other different expressions...). I really didn't manage to male it work with If/Else structures !!!

Here is the solution. The domain values depend on your needs, just consider them here as examples :-)

# Set full-CSP or just CSP-Report-Only
SetEnvIf Host ^ "CSP_REPORT_ONLY=[yes|no]"

# Set additional external sources :
# The use of SetEnvIf (instead of SetEnv) allows to take values into account more quickly
# Note the space after '=' for an empty value, otherwise the var is set with the value default 1
SetEnvIf Host ^ "SCRIPT_SRC=*.wp.com cdnjs.cloudflare.com *.google.com *.gstatic.com www.youtube.com"
SetEnvIf Host ^ "SCRIPT_SRC_ELEM=*.wp.com cdnjs.cloudflare.com *.google.com *.gstatic.com www.youtube.com"
SetEnvIf Host ^ "SCRIPT_SRC_ATTR=*.wp.com cdnjs.cloudflare.com *.google.com *.gstatic.com www.youtube.com"
SetEnvIf Host ^ "STYLE_SRC= "
SetEnvIf Host ^ "STYLE_SRC_ELEM= "
SetEnvIf Host ^ "STYLE_SRC_ATTR= "
SetEnvIf Host ^ "IMG_SRC=*.elementor.com *.gravatar.com *.wp.com *.w.org dummyimage.com"
SetEnvIf Host ^ "MEDIA_SRC= "
SetEnvIf Host ^ "FONT_SRC=*.gstatic.com *.googleapis.com *.doubleclick.net"
SetEnvIf Host ^ "BASE_URI= "
SetEnvIf Host ^ "OBJECT_SRC= "
SetEnvIf Host ^ "FRAME_SRC=*.google.com www.youtube.com www.youtube-nocookie.com"

# Stop editing : "don't touch" anything else after this !!

# Full-CSP headers :
# Syntax : Header set|append CSP "directive values" "condition expr= to apply the rule"
Header always set Content-Security-Policy "script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: %{HTTP_HOST}e *.%{HTTP_HOST}e %{SCRIPT_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "style-src 'self' 'unsafe-inline' %{HTTP_HOST}e *.%{HTTP_HOST}e %{STYLE_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "style-src-elem 'self' 'unsafe-inline' %{HTTP_HOST}e *.%{HTTP_HOST}e %{STYLE_SRC_ELEM}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "style-src-attr 'self' 'unsafe-inline' %{HTTP_HOST}e *.%{HTTP_HOST}e %{STYLE_SRC_ATTR}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "script-src-elem 'self' 'unsafe-inline' blob: %{HTTP_HOST}e *.%{HTTP_HOST}e %{SCRIPT_SRC_ELEM}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "script-src-attr 'self' 'unsafe-inline' blob: %{HTTP_HOST}e *.%{HTTP_HOST}e %{SCRIPT_SRC_ATTR}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "img-src 'self' data: blob: %{HTTP_HOST}e *.%{HTTP_HOST}e %{IMG_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "media-src 'self' %{HTTP_HOST}e *.%{HTTP_HOST}e %{MEDIA_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "font-src 'self' data: %{HTTP_HOST}e *.%{HTTP_HOST}e %{FONT_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "base-uri 'self' %{HTTP_HOST}e *.%{HTTP_HOST}e %{BASE_URI}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "object-src 'self' %{HTTP_HOST}e *.%{HTTP_HOST}e %{OBJECT_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "frame-src 'self' blob: %{HTTP_HOST}e *.%{HTTP_HOST}e %{FRAME_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"
Header append Content-Security-Policy "frame-ancestors 'self' %{HTTP_HOST}e *.%{HTTP_HOST}e" "expr=reqenv('CSP_REPORT_ONLY') == 'no'"

# Relax CSP headers :
# Syntax : Header set|append CSP-Report-Only "directive values" "condition expr= to apply the rule"
Header always set Content-Security-Policy-Report-Only "script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: %{HTTP_HOST}e *.%{HTTP_HOST}e %{SCRIPT_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "style-src 'self' 'unsafe-inline' %{HTTP_HOST}e *.%{HTTP_HOST}e %{STYLE_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "style-src-elem 'self' 'unsafe-inline' %{HTTP_HOST}e *.%{HTTP_HOST}e %{STYLE_SRC_ELEM}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "style-src-attr 'self' 'unsafe-inline' %{HTTP_HOST}e *.%{HTTP_HOST}e %{STYLE_SRC_ATTR}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "script-src-elem 'self' 'unsafe-inline' blob: %{HTTP_HOST}e *.%{HTTP_HOST}e %{SCRIPT_SRC_ELEM}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "script-src-attr 'self' 'unsafe-inline' blob: %{HTTP_HOST}e *.%{HTTP_HOST}e %{SCRIPT_SRC_ATTR}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "img-src 'self' data: blob: %{HTTP_HOST}e *.%{HTTP_HOST}e %{IMG_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "media-src 'self' %{HTTP_HOST}e *.%{HTTP_HOST}e %{MEDIA_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "font-src 'self' data: %{HTTP_HOST}e *.%{HTTP_HOST}e %{FONT_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "base-uri 'self' %{HTTP_HOST}e *.%{HTTP_HOST}e %{BASE_URI}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "object-src 'self' %{HTTP_HOST}e *.%{HTTP_HOST}e %{OBJECT_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "frame-src 'self' blob: %{HTTP_HOST}e *.%{HTTP_HOST}e %{FRAME_SRC}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"
Header append Content-Security-Policy-Report-Only "frame-ancestors 'self' %{HTTP_HOST}e *.%{HTTP_HOST}e" "expr=reqenv('CSP_REPORT_ONLY') == 'yes'"

# Other security headers :
Header append Content-Security-Policy "upgrade-insecure-requests; "
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "0"
Header always set Strict-Transport-Security "includeSubDomains; preload; max-age=63072000"
Header always set Expect-CT "max-age=7776000, enforce"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header unset X-Powered-By
Header unset Server
ServerSignature Off

That's all, folks, Hope it can help some of you which are stuck as I've been !

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.